一:緒論
表示時間復雜度的階有:
O(1) :常量時間階
O (n):線性時間階
O(㏒n) :對數時間階
O(n㏒n) :線性對數時間階
O (nk): k≥2 ,k次方時間階
以下六種計算算法時間的多項式是最常用的。其關系為:
O(1)<O(㏒n)<O(n)<O(n㏒n)<O(n2)<O(n3)
指數時間的關系為:
O(2n)<O(n!)<O(nn)
算法的空間復雜度定義為:S(n) = O(g(n))
表示隨着問題規模 n 的增大,算法運行所需存儲量S(n)的增長率與 g(n) 的增長率相同,稱S(n)(漸近)空間復雜度。
二:線性表
順序線性表:
設在線性表L中的第i個元素之前插入結點的概率為Pi,不失一般性,設各個位置插入是等概率,則Pi=1/(n+1),而插入時移動結點的次數為n-i+1。
總的平均移動次數: Einsert=∑pi*(n-i+1) (1≦i≦n)
∴ Einsert=n/2 。
鏈式線性表:
單鏈表:
例2.1假設利用兩個線性表LA和LB分別表示兩個集合A和B,現要求一個新的集合A=A∪B。
算法思想:1、擴大La,將存在於Lb中而不存在於La中的數據元素插入到La中去。2、需要從Lb中依次取得每個數據元素,並依值在La中進行查訪,若不存在,則插 之。
例2.3 已知線性表LA和LB中的數據元素是按值非遞減有序排列,現要求將LA和LB歸並為一個新的線性表LC,且LC中的數據元素也是按值非遞減有序排列。
1.初始化 LC 為空表;
2.分別從 LA和LB中取得當前元素 ai 和 bj;
3.若 ai≤bj,則將 ai 插入到 LC 中,否則將bj 插入到 LC 中;
4.重復 2 和 3 兩步,直至 LA 或 LB 中元素被取完為止;
5.將 LA 表或 LB 表中剩余元素復制插入到LC 表中。
(雙向)循環鏈表:
約瑟夫問題:
n 個人圍成一個圓圈,首先第1個人從1開始一個人一個人順時針報數, 報到第m個人,令其出列。然后再從下一個人開始,從1順時針報數,報到第m個人,再令其 出列,…,如此下去, 直到圓圈中只剩一個人為止。此人即為優勝者。
例如 n = 8 m = 3
三:棧和隊列
括號匹配的檢驗:(棧)
假設在表達式中允許包含兩種括號:圓括號和方括號,其嵌套的順序隨意,即:
([]())或[([ ][ ])]等為正確的格式,
[( ])或([( ))或 (()])均為不正確的格式。
則 檢驗括號是否匹配的方法可用“期待的急迫程度”這個概念來描述。
算法設計:
1) 凡出現左括弧,則進棧;
2) 凡出現右括弧,首先檢查棧是否空
若棧空,則表明該“右括弧”多余,
否則和棧頂元素比較,
若相匹配,則“左括弧出棧” ,
否則表明不匹配。
3)表達式檢驗結束時,
若棧空,則表明表達式中匹配正確,
否則表明“左括弧”有余。
表達式求值:(棧)
迷宮求解:(棧)
求迷宮路徑算法的基本思想是:從入口出發,按某一方向向未走過的前方探索
若當前位置“可通”,則納入路徑,繼續前進
若當前位置“不可通”,則后退,換方向繼續探索;
若四周“均無通路”,則將當前位置從路徑中刪除出去。
算法:
設定當前位置的初值為入口位置;
do{
若當前位置可通,
則{將當前位置插入棧頂;
若該位置是出口位置,則算法結束;
否則切換當前位置的東鄰方塊為
新的當前位置;
}
否則 {
}
}while (棧不空);
若棧不空且棧頂位置尚有其他方向未被探索,
則設定新的當前位置為: 沿順時針方向旋轉
找到的棧頂位置的下一相鄰塊;
若棧不空但棧頂位置的四周均不可通,
則{刪去棧頂位置;// 從路徑中刪去該通道塊
若棧不空,則重新測試新的棧頂位置,
直至找到一個可通的相鄰塊或出棧至棧空;
}
若棧空,則表明迷宮沒有通路。
棧的另外一個重要的應用:遞歸調用
用分治法求解遞歸問題:
分治法:對於一個較為復雜的問題,能夠分解成幾個相對簡單的且解法相同或類似的子問題來求解
三個條件:
1、能將一個問題轉變成一個新問題,而新問題與原問題的解法相同或類同,不同的僅是處理的對象,且這些處理對象是變化有規律的
2、可以通過上述轉化而使問題簡化
3、必須有一個明確的遞歸出口,或稱遞歸的邊界
分治法求解遞歸問題算法的一般形式:
void p (參數表) {
if (遞歸結束條件)可直接求解步驟;-----基本項
else p(較小的參數);------歸納項
}
long Fact ( long n ) {
if ( n == 0) return 1; //基本項
else return n * Fact (n-1); //歸納項}
四:串
簡單字符串模式匹配算法(BF算法):
算法思想:從主串S的第pos個字符起和模式串T的第一個字符比較之,若相等,則繼續比較后續字符;否則從主串的下一個字符起再重新和模式的字符比較之。依此類推,直至模式T中的每個字符依次和主串S中的一個連續的字符序列相等,則稱匹配成功,函數值為和模式T中第一個字符相等的字符在主串S中序號,否則稱匹配不成功,函數值為零。
首尾字符串匹配算法:
先比較模式串的第一個字符
再比較模式串的最后一個字符
最后比較模式串中從第二個到第n-1個字符
Kmp算法:(難理解):復雜度o(m+n)
若設目標串(主串)為s,模式串為p ,並設i指針和j指針分別指示目標串和模式串中正待比較的字符,設i和j的初值均為1。若有si=tj,則i和j分別加1。否則,i不變,j退回到j=next[j]的位置,再比較si和tj,若相等,則i和j分別加1。否則,i不變,j再次退回到j=next[j]的位置,依此類推,直至下列兩種可能:
一種是j退到某個next[…]時字符比較相等,則指針各自增1,繼續進行匹配;
另一種是j退到值為零(即模式的第一個字符“失配),則此時需將模式繼續向右滑動一個位置,即從主串的一個字符si+1起和模式重新開始匹配。
五:數組和廣義表:
數組,廣義表是線性表的推廣;
特殊矩陣:
對稱矩陣:
上三角:
下三角:
對角矩陣:對角矩陣可按行優先順序或對角線的順序,將其壓縮存儲到
一維數組中,且也能找到每個非零元素和向量下標的對應關系。
稀疏矩陣:
稀疏因子:m行n列t個非零元素
順序存儲:三元組(行數,列數,元素值)
轉置:
算法的基本思想:第一次從轉置前稀疏矩陣source中取出應該放置到轉置后的稀疏矩陣dest中第一個位置的元素,行列號互換后,放於dest中第一個位置;第二次從source中選取應該放到dest中的第二個位置的元素,……,如此進行,依次生成dest中的各元素。
方法一:按m的列序轉置
按T.data中三元組次序依次在M.data中找到相應的三元組進行轉置,即按照矩陣M的列序來進行置換。
為找到M中每一列所有非零元素,需對其三元組表M.data從第一行起掃描一遍。由於M.data中以M行序為主序,所以由此得到的恰是T.data中應有的順序。
方法二:快速轉置
按M.data中三元組次序轉置,轉置結果放入T.data中恰當位置。
此法關鍵是要預先確定M中每一列第一個非零元在T.data中位置,為確定這些位置,轉置前應先求得M的每一列中非零元個數。
鏈式存儲:十字鏈表
廣義表:(人工智能):
廣義表可以看成是線性表的推廣,線性表是廣義表的特例。廣義表的結構相當靈活,在某種前提下,它可以兼容線性表、數組、樹和有向圖等各種常用的數據結構。
A=(),B=(x, y, z),C=(B, y, z),D=(x,(y, z)),E=(x, E)
六:樹與二叉樹
二叉樹的性質:
- 在二叉樹第i層至多有2i-1個結點
- 深度為k的二叉樹至多有2k-1個結點
- 對於任何一顆二叉樹,如果其葉子數為n0,度為2的結點數為n2,則n0=n2+1
- 具有n個結點的完全二叉樹的深度為(log2n)+1
滿二叉樹的定義:
法一:若二叉樹中最多只有最下兩層有度小於2的結點,且最下層的結點都依次排列在最左邊,則稱此二叉樹為完全二叉樹。
法二:深度為k的二叉樹,若第1到第k-1層為深度為k-1的滿二叉樹,第k層的結點都依次排列在最左邊,則稱此二叉樹為完全二叉樹。
二叉樹的存儲:
順序:適合滿二叉樹和完全二叉樹
鏈式:二叉鏈表,三叉鏈表
特點:n個結點,有n+1個空指針域
二叉樹的遍歷:前序遍歷,中序遍歷,后序遍歷(前中后是指根結點)
實現:遞歸方法
非遞歸方法:用棧
已知前序遍歷和后序遍歷不能求出二叉樹
線索二叉樹:
目的:非線性結構 –> 線性結構
先序線索二叉樹
中序線索二叉樹
后續線索二叉樹
樹的存儲:
雙親表示法
孩子表示法
孩子兄弟表示法(常用)
樹轉二叉樹:兄弟相連留長子
特點:其右子樹一定為空
二叉樹變樹:左孩右右連雙親,去掉原來右孩線
森林變二叉樹:樹變二叉根相連
二叉樹變森林:去掉全部右孩線,孤立二叉再還原
樹的遍歷與二叉樹遍歷對應的關系:
樹:先序遍歷,后序遍歷
森林:先序遍歷,中序遍歷
二叉樹:先序遍歷,中序遍歷
Haffman樹與Haffman編碼:
Haffman樹最優樹:帶權路徑長度(WPL)最短的樹
Haffman最優二叉樹:WPL最短的二叉樹
構造Haffman樹:7 5 5 2 4
Haffman編碼:根據字符出現頻率編碼,使電文總長度最短
兩個問題:
n個結點經n-1次合並,每次生成新的結點,總共 n+n-1=2n-1個結點,度為2的結點個數為n-1
沒有度為1的結點
七:圖
無向圖邊的取值范圍:0<=e<=n(n-1)/2
有向圖弧的取值范圍:0<=e<=n(n-1)
稀疏圖:邊數或弧數遠少於nlogn
連通:無向圖中,頂點到頂點之間有路徑
圖的生成樹:一個連通圖(無向圖),生成樹是一個極小連通子圖,有圖中全部n個頂點,n-1條邊
對於非連通圖,每個連通分量可以構造一顆生成樹,從而構成森林
圖構成森林:由若干棵有向樹組成,會有圖中全部頂點,但只有足以構成若干棵不相交的有向樹的弧
圖的存儲:鄰接矩陣,鄰接鏈表,十字鏈表,鄰接多重表,邊表
有向圖的鄰接矩陣:頂點的度 = 第i行元素之和 + 第j列元素之和
無向樹的鄰接矩陣:頂點的度 = 第i行的元素 1 的個數
優點:容易實現圖的操作 缺點:空間效率為o(n2)
鄰接表:效率o(n+e)
圖的遍歷:
深度優先DFS:(Depth_First Search)
基本思想:仿樹的中序遍歷,分連通圖和非連通圖兩類
廣度優先BFS:(Breadth First Search)
基本思想:仿樹的層次遍歷
圖的最小生成樹
生成樹的代價:如果連通圖是一個帶權圖,則其生成樹中的邊也帶權,生成樹中所有邊的權值之和稱為生成樹的代價
最小生成樹MST(Minimun Spanning Tree):帶權連通圖中代價最小的生成樹
構造最小生成樹的方法:
① Prim算法(以頂點為對象),時間復雜度o(n2),適合稠密圖
② Kruskal算法(以邊為對象),時間復雜度o(eloge),適合稀疏圖
注意:最小生成樹可能不唯一
有向無環圖及其應用:
有向無環圖DAG:(Directed Acycling Graph)
拓撲排序:
① 在有向圖選一個沒有前驅的頂點且輸出 (入度為0)
② 從圖中刪除該頂點及它的所有尾
③ 重復以上兩步
AOV網:頂點表示活動的網(Activity On Vertex network),不允許有回路,弧表示活動之間的優先制約關系
檢測AOV中是否存在環:網中所有頂點拓撲有序序列(拓撲序列不是唯一的)
關鍵路徑:(Critical Path)
AOE網(Activity On Eage network):頂點表示時間,弧表示活動,權表示活動持續的時間
關鍵活動:該邊上的權值增加將使有向圖上的最長路徑長度增加,l(i)=e(i)
找關鍵路徑:必須找到關鍵活動
① 向前遞推
② 向后遞推
③ 把l(i)=e(i)的路徑連起來
最短路徑(Short Path):交通網絡問題
分兩種:單源點最短路徑,所有頂點最短路徑
單源點最短路徑:
方法一:將源點到終點所有路徑列出來,選最短的。缺點:隨路徑的增多,效率會降低
方法二:Dijkstra算法:按路徑長度遞增次序產生各頂點的最短路徑
每一對頂點間最短路徑:
方法一:以一個頂點為源點,重復執行Dijkstra算法N次,T(n)=o(n3)
方法二:Floyd算法:
思想:逐個頂點試探,從vi到vj的所有可能存在路徑中,選出一條長度最短的
實現:
(1)初始時設置一個n階方陣,令其對角線元素為0,若存在弧<Vi,Vj>,則對應元素為權值,否則為∞。
(2)逐步試着在原直接路徑中增加中間頂點,若加入中間點后路徑變短,則修改之;否則,維持原值。
(3)所有頂點試探完畢,算法結束。
八:查找
靜態表查找
平均查找長度ASL:(Average Search Length):為了確定記錄在表中的位置,需要與給定值進行比較關鍵字的個數的期望值
評價標准:ASL
順序查找:(順序或鏈式)
思想:從表中最后一個記錄開始,逐個進行記錄的關鍵字和給定值的比較,若某個記錄的關鍵字和給定值比較相等,則查找成功,否則查找不成功。
折半查找:(順序)
效率比順序查找高
動態查找表
存在關鍵字為key的記錄,則返回,否則插入
二叉排序樹:(二叉查找樹)
特點:
i. 左子樹所有節點<根節點
ii. 右子樹所有節點>根節點
iii. 左右子樹分別是二叉排序樹
算法:
① 查找
② 插入:插入位置由查找過程得到
③ 刪除:(分三種情況,定則:保持中序序列不變)
a) *p為葉子節點,只需修改*P雙親*f的指針
b) *P只有左子樹或右子樹
i. 只有左子樹:用*P的左孩子代替*p
ii. 只有右子樹:用*p的右孩子代替*p
c) *p左右子樹非空
i. 用*P的直接前驅取代*p
ii. 用*p的直接后繼取代*p
iii. 若*p的左子樹無右子樹,用*p左子樹取代*p
含有 n 個結點的二叉排序樹的平均查找長度和樹的形態有關
最好情況: ASL=log 2(n + 1) – 1;
樹的深度為:log 2n + 1;
與折半查找中的判定樹相同。
(形態比較均衡)。
最壞情況:插入的 n 個元素從一開始就有序,
—— 變成單支樹的形態!
此時樹的深度為 n; ASL = (n + 1) / 2
查找效率與順序查找情況相同。
平衡二叉樹(AVL樹):
性質:
① 左右子樹是平衡二叉樹
② 所有節點的左右子樹深度之差的絕對值小於等於1
節點平衡因子:該節點左子樹和右子樹的深度差
構造平衡二叉樹:
LL平衡旋轉:(B為軸,順時針旋轉)
RR平衡旋轉:(B為軸,逆時針旋轉)
LR平衡旋轉:(c為軸,先逆時針旋轉,后順時針旋轉)
RL平衡旋轉:(c為軸,先順時針旋轉,后逆時針旋轉)
B-樹:
B+樹:
散列表:
特點:ASL=0,不需要經過比較
哈希表:根據設定的哈希函數H(key)和所選中的處理沖突的方法建立的查找表
思想:以記錄的關鍵字為自變量,根據哈希函數計算出對應的哈希地址,並在此存儲該記錄的內容
構造哈希函數的方法:
對數字:直接定值法,數字分析法,隨機數法
平方取中法(常用),折疊法,除留與余數法(最常用H(key)=key MOD p p<=m,p為<m的素數或不含20以下質因子的合數)
非數字:先進行數字化處理
處理沖突的方法:
① 開放定址法:當發生沖突,在沖突位置前后附近尋找可以存放記錄的空閑單元
H0=H(key)
Hi=(H(key)+di) MOD m i=1,2….
Di有三種取法:
a) 線性探測再散列 ,di = 1,2,….
b) 二次探測再散列(平方),di = 12 -12 22 -22
c) 為隨機探測再散列(雙散列函數探測再散列)
i. Di=偽隨機數列 或者 di = i×H2(key)
② 鏈地址開方法(開域法):將所有哈希值相同的記錄都連接在同一鏈表中
九:排序
評價標准:執行時間,輔助空間,算法穩定性
內部排序:
① 插入排序
a) 直接插入排序
b) 希爾排序
i. 思想:將整個待排序記錄分割成若干個子序列,在子序列內分別進行直接插入排序,待整個序列中的記錄基本有序時,對全體記錄進行直接插入排序。
② 交換排序
a) 冒泡排序
b) 快速排序
i. 思想: 首先選一個軸值(即比較的基准),通過一趟排序將待排序記錄分割成獨立的兩部分,前一部分記錄的關鍵碼均小於或等於軸值,后一部分記錄的關鍵碼均大於或等於軸值,然后分別對這兩部分重復上述方法,直到整個序列有序。
③ 選擇排序
a)簡單選擇排序(直接選擇排序)
b)堆排序(改進:查找最小碼的同時,找出較小值,分大根堆,小根堆)
④ 歸並排序
a) 二路歸並排序:將一個具有n個待排序記錄的序列看成是n個長度為1的有序序列,然后進行兩兩歸並,得到n/2個長度為2的有序序列,再進行兩兩歸並,得到n/4個長度為4的有序序列,……,直至得到一個長度為n的有序序列為止。
⑤ 基數排序
a) 多關鍵字排序
i. 最高位優先MSD法
ii. 最低位優先MSD法
b) 鏈式基數排序
c)基數排序的時間復雜度為O(d(2n+r))
其中:分配為O(n)
收集為O(n+r)(r為“基”)
d為“分配-收集”的趟數
外部排序:外排總的時間還應包括內部排序所需時間和逐趟歸並時進行內部歸並的時間
討論:
時間復雜度:
① 平均時間性能:
a)時間復雜度為 O(nlogn):快速排序、堆排序和歸並排序
b) 時間復雜度為 O(n2):直接插入排序、起泡排序和簡單選擇排序
c)時間復雜度為 O(n):基數排序
② 排元素序列按關鍵字順序有序
a) 直接插入排序能達到O(n)的時間復雜度, 快速排序的時間性能蛻化為O(n2) 。
③ 排序的時間性能不隨關鍵字分布而改變的排序
a) 簡單選擇排序、起泡排序、堆排序和歸並排序的時間性能不隨元素序列中關鍵字的分布而改變
穩定的排序方法指的是,對於兩個關鍵字相等的元素,它們在序列中的相對位置,在排序之前和經過排序之后,沒有改變。