將COMP20003中關於Graph的內容進行總結,內容來自COMP20003,中文術語並不准確,以英文為准。
Graph G = {V, E}
頂Vertices V: can contain information
邊Edges E (links between vertices): can have direction and/or weight
種類:
- 有向圖(directed graph):邊(edge)有方向。
- 弱有向連接圖Weakly connected directed graph:將有向的邊替換成無向的邊后能得到無向連通圖。
-
- 強有向連接圖Strongly connected directed graph:在有向圖中,任意頂通過邊到達任意頂。
-
-
- Strongly connected components in a directed graph:在同一區域(component)的頂可以到達所有同一區域的頂。
-
- 無向圖(undirected graph):邊(edge)沒有方向。
- 無向連通圖Connected Undirected graph: 任意的頂均可通過邊連接到其他頂,包括間接。
-
- 無向非連通圖Unconnected Undirected graph:即不是無向連通圖Connected Undirected graph。
完全圖Complete graph:每個頂能直接到其他頂。對於無向圖至少需要V(V-1)/2個頂,有向圖至少需要V(V - 1)個頂。
用數據結構表示:
用二維數組(Matrix)表示:
注意:未連接用無窮大表示,而不是用0表示。
復雜度O(V2)
用鏈表表示:
復雜度O(V + E)
圖的遍歷Traversal
Depth-first search(DFS)深度優先搜索,使用stack實現,基於stack先進后出的特性。
Breadth first search(BFS)廣度優先搜索,使用queue實現,基於queue先進先出的特性。
圖的拓撲:應用於有向無環圖(Directed Acyclic Graphs,簡稱DAG)
Topological sort: a partial ordering that fulfils certain constraints. All edges e(i, j) go in horizontal i -> j direction
變成水平排列,只能從左邊指向右邊。
拓撲涉及的概念:
indegree:該頂被邊到達的次數。
outdegree:由該頂開始邊連接的次數。
能有拓撲結構,DAG圖必須滿足有一個source(indegree為0)和sink(outdegree為0)。
如果在DAG圖中存在漢密爾頓路徑(Hamiltonian path,即從某個頂開始通過邊不重復的經過所有點的路徑,想想游戲一筆畫),則該拓撲是唯一的。
代碼:https://github.com/Will-Zhu-27/Algorithms-and-Data-Structures/tree/master/graph/Topological%20sort
算法:
Dijkstra's algorithm for single source shortest path
Dijkstra單源最短路徑算法:一個頂到其他頂的最短路徑。
基於Greedy algorithm: 最短路徑中的子路徑也是最短路徑,即A到Y的最短路徑經過X,那么該路徑中從A到X的部分是A到X的最短路徑。
假定沒有負值的邊。
使用優先隊列priority queue。
步驟:
使用變量數組dist記錄從目標定到該頂的最短距離,數組pred記錄經過該頂的前一個頂,edgeWeight(a, b):頂a到頂b邊的值。
-
- 將dist自己到自己為0,其余最大(MAX_INT)。pred均為NULL。
- 將所有頂裝入優先隊列,按照對應的dist值的大小為優先度。
- 當優先隊列不為空時,pop一個頂a,為空則結束。
- LOOP:如果dist[a] + edgeWeight(a, b) < dist[b],b為跟頂a有邊連接的頂,則更新頂b的信息:dist[b] = dist[a] + edgeWeight(a, b),pred[b] = a,更新優先隊列中的順序(也可pop時再根據有限度pop)。
- 步驟3。
復雜度分析:
實現代碼:https://github.com/Will-Zhu-27/Algorithms-and-Data-Structures/tree/master/graph/shortestPaths/dijkstra
Warshall algorithm for transitive closure -- unconnected directed graph
Warshall算法:用於判斷有向圖中頂與頂之間是否能通過邊聯通(包括間接的)。
用二維數組儲存基礎(直接)邊連接的信息,使用三次循環。
其中i for intermediate, s for source, t for to,循環變量怎么命名的並不重要,重要的是最外圈的循環變量i必須作為判斷的中間變量,改變它的位置會導致算法出錯。我的理解為:算法是基於貪心算法,
當循環到 i 時,就要得到通過 0 到 i 是否有最短路徑,然后逐漸增大i達到在全部頂中的最短路徑。此問題也可看在知乎上的這個問題的回答:https://www.zhihu.com/question/30955032
Floyd-Warshall algorithm for all pairs shortest paths
Floyd-Warshall算法:圖中每個頂到其他頂的最短路徑。
在Warshall的基礎上稍作改變,二維數組儲存的是頂之間weight的信息。
同樣的,最外圈的循環變量i必須作為判斷的中間變量!另外用C實現時,雖然圖里不連接用了∞表示, C中用 INT_MAX / 2 表示, 因為if 判斷時會產生數據溢出問題。
要記錄路徑也只要加個二維數組記錄即可。
復雜度:θ(V3)
代碼:https://github.com/Will-Zhu-27/Algorithms-and-Data-Structures/tree/master/graph/AllPairsShorestPaths/Floyd-Warshall
Floyd-Warshall和dijkstra在計算all pairs shorest paths上的優劣:
循環V次dijkstra也可得到全部頂的最短路徑,復雜度為:O((V2 + V * E)logV)
Floyd-Warshall復雜度為θ(V3)。
對於sparse graph with positive edge weights(V>>E),Dijkstra用於all pair shortest path更好
對於dense graph with positive edge weights(E>>V) Floyd-Warshall更好
最小生成樹 Minimum Spanning Tree, MST
要求:
undirected weighted graphs
Graph must be connected 無向非連通圖
MST特點:
包括所有的頂
minimum sum of edge weights,包含連接頂的邊和最小,數量為V-1
MST中沒有循環(cycles)。
簡言之,使用最小weight的edge將所有頂連接到。
如果所有的edge的weight不同,那么MST是唯一的。
計算MST的算法有Prim和Kruskal。
Prim:通過添加下一個最近的頂完成MST。
需要使用優先隊列。
准備:dist[V]初值MAX,pred[V]初值NULL,inMst[V]初值FALSE。
步驟:
-
- 將某一頂 (隨便那個)作為起始點root,dist[ root ] = 0。將全部頂enqueue優先隊列,以dist為優先級。
- pop一個頂a,所有與a直接相連的頂b:如果inMst[b]==FALSE && a到b的edge < dist[b],則更新dist[b]等於該edge,更新pred[b]=a,調整優先隊列中的順序。
- inMst[a] = TRUE;
- 優先隊列為空->結束, 不為空->步驟2
若手工計算,以a為起始點為例,與a相連的有b和c,到b更短,選b。
將a和b作為一個整體,與整體相連的有c、d、e,c和d到整體(最短)都是8,隨便選一個,選c。
將a和b和c作為一個整體,與整體相連的有e、d、f,到整體最短的是f,選f。
將a和b和c和f作為一個整體,與整體相連的有e、d、g,到整體最短的是d,選d。
將a和b和c和f和d作為一個整體,與整體相連的有e、g,到整體最短的是g,選g。
將a和b和c和f和d和g最為一個整體,與整體相連的有e,選e。
復雜度分析:
從算法步驟可以看出復雜度是與V有關的,所以Prim更適合dense graph(E>>V)。
代碼:https://github.com/Will-Zhu-27/Algorithms-and-Data-Structures/tree/master/graph/Minimum%20Spanning%20Tree/Prim
Kruskal:通過添加最短的且不構成回路的邊完成MST
需要使用優先隊列和Union-find 結構。
Union-find 結構:所有的頂都各自為一個集合,如果因為E[a][b](連接頂a和頂b)是mst中的話,則a和b所在的集合合並,可以以數組表示如下圖。
也可以用樹的形式表示,以樹的形式表示的話,在進行union合並操作時,注意時將小的樹並入大的樹中。
步驟:
1.將所有邊根據weight入列優先隊列。將所有頂裝入union-find結構。
2.從優先隊列中取出一條邊E[a][b](weight最小的)。
3.如果a和b在同一集合中,則步驟2。如果不再同一集合中,則a和b所在的集合合並,E是mst的一條邊。
4.當mst邊的數量< V - 1,步驟2。
若手工計算,以下圖為例:
E[a][b](或者是E[c][e],只要不構成回路,選哪個都行)為mst中的邊。
E[c][e]為mst中的邊。
E[b][c]為mst中的邊。
E[c][f]為mst中的邊。
E[g][f]為mst中的邊。
E[d][f]為mst中的邊,不能是E[c][g]或E[a][e]會構成回路。
已經過所有的頂,mst完成。
代碼:https://github.com/Will-Zhu-27/Algorithms-and-Data-Structures/tree/master/graph/Minimum%20Spanning%20Tree/Kruskal