圖的演算法的總結
圖的定義:
很簡單,G(V,E), V、E分別表示點和邊的集合。
圖的表示:
主要有兩種,鄰接矩陣和鄰接表,前者空間複雜度,O(V2),後者為O(V+E)。因此,除非非常稠密的圖(邊非常多),一般後者優越於前者。
圖的遍歷:
寬度遍歷BFS(start): (1) 佇列Q=Empty,陣列bool visited[V]={false...}. Q.push(start);
(2) while (!Q.empty()){
u = Q.pop(); visited[u] = true; //遍歷u結點
foreach (u的每一個鄰接結點v) Q.push(v);
}
深度遍歷DFS(start): (1) 棧S=Empty, 陣列bool visited[V]={false...}. S.push(start);
(2) while (!S.empty()){
u = S.pop();
if (!visited[u]) visited[u] = true; //遍歷u結點
foreach (u的每一個鄰接結點v) S.push(v);
}
初看之下兩個演算法很相似,主要區別在於一個使用佇列,一個使用棧,最終導致了遍歷的順序截然不同。佇列是先入先出,所以訪問u以後接下來就訪問u中未訪問過的鄰接結點;而棧的後進先出,當訪問u後,壓入了u的鄰接結點,在後面的迴圈中,首先訪問u的第一個臨接點v,接下來又將v的鄰接點w壓入S,這樣接下來要訪問的自然是w了。
最小生成樹:
(1)Prime演算法: (1) 集合MST=T=Empty,選取G中一結點u,T.add(u)
(2) 迴圈|V|-1次:
選取一條這樣的邊e=min{(x,y) | x in T, y in V/T}
T.add(y); MST.add(e);
(3) MST即為所求
(2) Kruskal演算法 (1) 將G中所有的邊排序並放入集合H中,初始化集合MST=Empty,初始化不相交集合T={{v1}, {v2}...}},也即T中每個點為一個集合。
(2) 依次取H中的最短邊e(u,v),如果Find-Set(u)!=Find-Set(v)(也即u、v是否已經在一棵樹中),那麼Union(u,v) (即u,v合併為一個集合),MST.add(e);
(3) MST即為所求
這兩個演算法都是貪心演算法,區別在於每次選取邊的策略。證明該演算法的關鍵在於一點:如果MST是圖G的最小生成樹,那麼在子圖G'中包含的子生成樹MST' 也必然是G'的最小生成樹。這個很容易反正,假設不成立,那麼G'有一棵權重和更小的生成樹,用它替換掉MST',那麼對於G我們就找到了比MST更小的生成樹,顯然這與我們的假設(MST是最小生成樹)矛盾了。
理解了這個關鍵點,演算法的正確性就好理解多了。對於Prime,T於V/T兩個點集都會各自有一棵生成樹,最後要連起來構成一棵大的生成樹,那麼顯然要選兩者之間的最短的那條邊了。對於Kruskal演算法,如果當前選取的邊沒有引起環路,那麼正確性是顯然的(對給定點集依次選最小的邊構成一棵樹當然是最小生成樹了),如果導致了環路,那麼說明兩個點都在該點集裡,由於已經構成了樹(否則也不可能導致環路)並且一直都是挑盡可能小的,所以肯定是最小生成樹。
最短路徑:
這裡的演算法基本是基於動態規劃和貪心演算法的,經典演算法有很多個,主要區別在於:有的是通用的,有的是針對某一類圖的,例如,無負環的圖,或者無負權邊的圖等。
單源最短路徑(1) 通用(Bellman-Ford演算法):
(2) 無負權邊的圖(Dijkstra演算法):
(3) 無環有向圖(DAG) :
所有結點間最短路徑:
(1) Floyd-Warshall演算法:
(2) Johnson演算法:
很簡單,G(V,E), V、E分別表示點和邊的集合。
圖的表示:
主要有兩種,鄰接矩陣和鄰接表,前者空間複雜度,O(V2),後者為O(V+E)。因此,除非非常稠密的圖(邊非常多),一般後者優越於前者。
圖的遍歷:
寬度遍歷BFS(start): (1) 佇列Q=Empty,陣列bool visited[V]={false...}. Q.push(start);
(2) while (!Q.empty()){
u = Q.pop(); visited[u] = true; //遍歷u結點
foreach (u的每一個鄰接結點v) Q.push(v);
}
深度遍歷DFS(start): (1) 棧S=Empty, 陣列bool visited[V]={false...}. S.push(start);
(2) while (!S.empty()){
u = S.pop();
if (!visited[u]) visited[u] = true; //遍歷u結點
foreach (u的每一個鄰接結點v) S.push(v);
}
初看之下兩個演算法很相似,主要區別在於一個使用佇列,一個使用棧,最終導致了遍歷的順序截然不同。佇列是先入先出,所以訪問u以後接下來就訪問u中未訪問過的鄰接結點;而棧的後進先出,當訪問u後,壓入了u的鄰接結點,在後面的迴圈中,首先訪問u的第一個臨接點v,接下來又將v的鄰接點w壓入S,這樣接下來要訪問的自然是w了。
最小生成樹:
(1)Prime演算法: (1) 集合MST=T=Empty,選取G中一結點u,T.add(u)
(2) 迴圈|V|-1次:
選取一條這樣的邊e=min{(x,y) | x in T, y in V/T}
T.add(y); MST.add(e);
(3) MST即為所求
(2) Kruskal演算法 (1) 將G中所有的邊排序並放入集合H中,初始化集合MST=Empty,初始化不相交集合T={{v1}, {v2}...}},也即T中每個點為一個集合。
(2) 依次取H中的最短邊e(u,v),如果Find-Set(u)!=Find-Set(v)(也即u、v是否已經在一棵樹中),那麼Union(u,v) (即u,v合併為一個集合),MST.add(e);
(3) MST即為所求
這兩個演算法都是貪心演算法,區別在於每次選取邊的策略。證明該演算法的關鍵在於一點:如果MST是圖G的最小生成樹,那麼在子圖G'中包含的子生成樹MST' 也必然是G'的最小生成樹。這個很容易反正,假設不成立,那麼G'有一棵權重和更小的生成樹,用它替換掉MST',那麼對於G我們就找到了比MST更小的生成樹,顯然這與我們的假設(MST是最小生成樹)矛盾了。
理解了這個關鍵點,演算法的正確性就好理解多了。對於Prime,T於V/T兩個點集都會各自有一棵生成樹,最後要連起來構成一棵大的生成樹,那麼顯然要選兩者之間的最短的那條邊了。對於Kruskal演算法,如果當前選取的邊沒有引起環路,那麼正確性是顯然的(對給定點集依次選最小的邊構成一棵樹當然是最小生成樹了),如果導致了環路,那麼說明兩個點都在該點集裡,由於已經構成了樹(否則也不可能導致環路)並且一直都是挑盡可能小的,所以肯定是最小生成樹。
最短路徑:
這裡的演算法基本是基於動態規劃和貪心演算法的,經典演算法有很多個,主要區別在於:有的是通用的,有的是針對某一類圖的,例如,無負環的圖,或者無負權邊的圖等。
單源最短路徑(1) 通用(Bellman-Ford演算法):
(2) 無負權邊的圖(Dijkstra演算法):
(3) 無環有向圖(DAG) :
所有結點間最短路徑:
(1) Floyd-Warshall演算法:
(2) Johnson演算法:
相關文章
- 樹的演算法總結演算法
- 最短路演算法的總結演算法
- 圖論(三)--各種基礎圖演算法總結圖論演算法
- 圖文並茂排序與演算法總結排序演算法
- 面試中的排序演算法總結面試排序演算法
- <摘錄>演算法策略的總結演算法
- 圖的最短路徑演算法彙總演算法
- 常用的比較排序演算法總結排序演算法
- 對接高德地圖API的總結地圖API
- 圖論總結圖論
- 2012我的總結 - 圖靈圖書圖靈
- 一些常用的演算法技巧總結演算法
- 幾種常見的排序演算法總結排序演算法
- 機器學習常見的演算法面試題總結機器學習演算法面試題
- 面試中的 10 大排序演算法總結面試排序演算法
- 圖論演算法 有圖有程式碼 萬字總結 向前輩致敬圖論演算法
- 圖論演算法 有圖有程式碼 萬字總結 向前輩致敬圖論演算法
- 演算法思路總結演算法
- Manacher演算法總結演算法
- 排序演算法總結排序演算法
- 機器學習演算法總結機器學習演算法
- 《演算法圖解》總結第 7 章:狄克斯特拉演算法演算法圖解
- UML類圖幾種關係的總結
- 機器學習演算法的隨機資料生成總結機器學習演算法隨機
- 進行一個字串演算法的總結字串演算法
- 對四種限流演算法的思考和總結演算法
- 圖知識總結
- UML類圖總結
- OpenCV三種立體匹配求視差圖演算法總結OpenCV演算法
- 優化演算法總結優化演算法
- Go加密演算法總結Go加密演算法
- 《演算法之美》總結演算法
- 演算法總結--ST表演算法
- 演算法總結--搜尋演算法
- javascrip常用演算法總結Java演算法
- 排序演算法全總結排序演算法
- 前端 排序演算法總結前端排序演算法
- 插值演算法總結演算法