Kruskal 最小生成樹演算法
對於一個給定的連通的無向圖 G = (V, E),希望找到一個無迴路的子集 T,T 是 E 的子集,它連線了所有的頂點,且其權值之和為最小。
因為 T 無迴路且連線所有的頂點,所以它必然是一棵樹,稱為生成樹(Spanning Tree),因為它生成了圖 G。顯然,由於樹 T 連線了所有的頂點,所以樹 T 有 V – 1 條邊。一張圖 G 可以有很多棵生成樹,而把確定權值最小的樹 T 的問題稱為最小生成樹問題(Minimum Spanning Tree)。術語 “最小生成樹” 實際上是 “最小權值生成樹” 的縮寫。
Kruskal 演算法提供一種在 O(ElogV) 執行時間確定最小生成樹的方案。Kruskal 演算法基於貪心演算法(Greedy Algorithm)的思想進行設計,其選擇的貪心策略就是,每次都選擇權重最小的但未形成環路的邊加入到生成樹中。其演算法結構如下:
- 將所有的邊按照權重非遞減排序;
- 選擇最小權重的邊,判斷是否其在當前的生成樹中形成了一個環路。如果環路沒有形成,則將該邊加入樹中,否則放棄。
- 重複步驟 2,直到有 V – 1 條邊在生成樹中。
上述步驟 2 中使用了 Union-Find 演算法來判斷是否存在環路。
例如,下面是一個無向連通圖 G。
圖 G 中包含 9 個頂點和 14 條邊,所以期待的最小生成樹應包含 (9 – 1) = 8 條邊。
首先對所有的邊按照權重的非遞減順序排序:
Weight Src Dest 1 7 6 2 8 2 2 6 5 4 0 1 4 2 5 6 8 6 7 2 3 7 7 8 8 0 7 8 1 2 9 3 4 10 5 4 11 1 7 14 3 5
然後從排序後的列表中選擇權重最小的邊。
1. 選擇邊 {7, 6},無環路形成,包含在生成樹中。
2. 選擇邊 {8, 2},無環路形成,包含在生成樹中。
3. 選擇邊 {6, 5},無環路形成,包含在生成樹中。
4. 選擇邊 {0, 1},無環路形成,包含在生成樹中。
5. 選擇邊 {2, 5},無環路形成,包含在生成樹中。
6. 選擇邊 {8, 6},有環路形成,放棄。
7. 選擇邊 {2, 3},無環路形成,包含在生成樹中。
8. 選擇邊 {7, 8},有環路形成,放棄。
9. 選擇邊 {0, 7},無環路形成,包含在生成樹中。
10. 選擇邊 {1, 2},有環路形成,放棄。
11. 選擇邊 {3, 4},無環路形成,包含在生成樹中。
12. 由於當前生成樹中已經包含 V – 1 條邊,演算法結束。
C# 實現的 Kruskal 演算法如下。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 5 namespace GraphAlgorithmTesting 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 Graph g = new Graph(9); 12 g.AddEdge(0, 1, 4); 13 g.AddEdge(0, 7, 8); 14 g.AddEdge(1, 2, 8); 15 g.AddEdge(1, 7, 11); 16 g.AddEdge(2, 3, 7); 17 g.AddEdge(2, 5, 4); 18 g.AddEdge(8, 2, 2); 19 g.AddEdge(3, 4, 9); 20 g.AddEdge(3, 5, 14); 21 g.AddEdge(5, 4, 10); 22 g.AddEdge(6, 5, 2); 23 g.AddEdge(8, 6, 6); 24 g.AddEdge(7, 6, 1); 25 g.AddEdge(7, 8, 7); 26 27 Console.WriteLine(); 28 Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount); 29 Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount); 30 Console.WriteLine(); 31 32 Console.WriteLine("Is there cycle in graph: {0}", g.HasCycle()); 33 Console.WriteLine(); 34 35 Edge[] mst = g.Kruskal(); 36 Console.WriteLine("MST Edges:"); 37 foreach (var edge in mst) 38 { 39 Console.WriteLine("\t{0}", edge); 40 } 41 42 Console.ReadKey(); 43 } 44 45 class Edge 46 { 47 public Edge(int begin, int end, int weight) 48 { 49 this.Begin = begin; 50 this.End = end; 51 this.Weight = weight; 52 } 53 54 public int Begin { get; private set; } 55 public int End { get; private set; } 56 public int Weight { get; private set; } 57 58 public override string ToString() 59 { 60 return string.Format( 61 "Begin[{0}], End[{1}], Weight[{2}]", 62 Begin, End, Weight); 63 } 64 } 65 66 class Subset 67 { 68 public int Parent { get; set; } 69 public int Rank { get; set; } 70 } 71 72 class Graph 73 { 74 private Dictionary<int, List<Edge>> _adjacentEdges 75 = new Dictionary<int, List<Edge>>(); 76 77 public Graph(int vertexCount) 78 { 79 this.VertexCount = vertexCount; 80 } 81 82 public int VertexCount { get; private set; } 83 84 public IEnumerable<int> Vertices { get { return _adjacentEdges.Keys; } } 85 86 public IEnumerable<Edge> Edges 87 { 88 get { return _adjacentEdges.Values.SelectMany(e => e); } 89 } 90 91 public int EdgeCount { get { return this.Edges.Count(); } } 92 93 public void AddEdge(int begin, int end, int weight) 94 { 95 if (!_adjacentEdges.ContainsKey(begin)) 96 { 97 var edges = new List<Edge>(); 98 _adjacentEdges.Add(begin, edges); 99 } 100 101 _adjacentEdges[begin].Add(new Edge(begin, end, weight)); 102 } 103 104 private int Find(Subset[] subsets, int i) 105 { 106 // find root and make root as parent of i (path compression) 107 if (subsets[i].Parent != i) 108 subsets[i].Parent = Find(subsets, subsets[i].Parent); 109 110 return subsets[i].Parent; 111 } 112 113 private void Union(Subset[] subsets, int x, int y) 114 { 115 int xroot = Find(subsets, x); 116 int yroot = Find(subsets, y); 117 118 // Attach smaller rank tree under root of high rank tree 119 // (Union by Rank) 120 if (subsets[xroot].Rank < subsets[yroot].Rank) 121 subsets[xroot].Parent = yroot; 122 else if (subsets[xroot].Rank > subsets[yroot].Rank) 123 subsets[yroot].Parent = xroot; 124 125 // If ranks are same, then make one as root and increment 126 // its rank by one 127 else 128 { 129 subsets[yroot].Parent = xroot; 130 subsets[xroot].Rank++; 131 } 132 } 133 134 public bool HasCycle() 135 { 136 Subset[] subsets = new Subset[VertexCount]; 137 for (int i = 0; i < subsets.Length; i++) 138 { 139 subsets[i] = new Subset(); 140 subsets[i].Parent = i; 141 subsets[i].Rank = 0; 142 } 143 144 // Iterate through all edges of graph, find subset of both 145 // vertices of every edge, if both subsets are same, 146 // then there is cycle in graph. 147 foreach (var edge in this.Edges) 148 { 149 int x = Find(subsets, edge.Begin); 150 int y = Find(subsets, edge.End); 151 152 if (x == y) 153 { 154 return true; 155 } 156 157 Union(subsets, x, y); 158 } 159 160 return false; 161 } 162 163 public Edge[] Kruskal() 164 { 165 // This will store the resultant MST 166 Edge[] mst = new Edge[VertexCount - 1]; 167 168 // Step 1: Sort all the edges in non-decreasing order of their weight 169 // If we are not allowed to change the given graph, we can create a copy of 170 // array of edges 171 var sortedEdges = this.Edges.OrderBy(t => t.Weight); 172 var enumerator = sortedEdges.GetEnumerator(); 173 174 // Allocate memory for creating V ssubsets 175 // Create V subsets with single elements 176 Subset[] subsets = new Subset[VertexCount]; 177 for (int i = 0; i < subsets.Length; i++) 178 { 179 subsets[i] = new Subset(); 180 subsets[i].Parent = i; 181 subsets[i].Rank = 0; 182 } 183 184 // Number of edges to be taken is equal to V-1 185 int e = 0; 186 while (e < VertexCount - 1) 187 { 188 // Step 2: Pick the smallest edge. And increment the index 189 // for next iteration 190 Edge nextEdge; 191 if (enumerator.MoveNext()) 192 { 193 nextEdge = enumerator.Current; 194 195 int x = Find(subsets, nextEdge.Begin); 196 int y = Find(subsets, nextEdge.End); 197 198 // If including this edge does't cause cycle, include it 199 // in result and increment the index of result for next edge 200 if (x != y) 201 { 202 mst[e++] = nextEdge; 203 Union(subsets, x, y); 204 } 205 else 206 { 207 // Else discard the nextEdge 208 } 209 } 210 } 211 212 return mst; 213 } 214 } 215 } 216 }
輸出結果如下:
參考資料
- Connectivity in a directed graph
- Strongly Connected Components
- Tarjan’s Algorithm to find Strongly Connected Components
相關文章
- Matlab生成Kruskal最小生成樹Matlab
- 最小生成樹:Kruskal演算法和Prim演算法演算法
- 最小生成樹-Prim演算法和Kruskal演算法演算法
- 最小生成樹(MinSpanTree)的Kruskal演算法演算法
- 最小生成樹之Prim演算法和Kruskal演算法演算法
- 北極通訊網路——最小生成樹kruskal
- 最小生成樹,Prim演算法與Kruskal演算法,408方向,思路與實現分析演算法
- 東哥帶你刷圖論第五期:Kruskal 最小生成樹演算法圖論演算法
- 最小生成樹---普里姆演算法(Prim演算法)和克魯斯卡爾演算法(Kruskal演算法)演算法
- 最小生成樹__Kurskal演算法演算法
- 最小生成樹__Prim演算法演算法
- 最小生成樹的演算法演算法
- 最小生成樹之 Prim 演算法演算法
- 【演算法學習】最小生成樹演算法
- 演算法-圖論-最小生成樹演算法圖論
- 關於圖的最小生成路徑——Kruskal演算法和Prime演算法演算法
- 最小生成樹
- 最小樹形圖(有向圖的最小生成樹)朱劉演算法演算法
- 最小生成樹——Prim演算法和Kruscal演算法演算法
- 最小生成樹prim普里姆演算法演算法
- 最小生成樹之普里姆演算法演算法
- 圖論中的最小生成樹演算法圖論演算法
- Boruvka求最小生成樹(菠蘿演算法)演算法
- 最小度限制生成樹
- 【模板】最小生成樹
- 演算法學習之路|最小生成樹——prime演算法演算法
- 前端必會演算法 - 最小生成樹問題前端演算法
- 【圖論】最小生成樹圖論
- 最小生成樹專項
- 圖論 最小生成樹圖論
- 如何在 Java 中實現最小生成樹演算法Java演算法
- Kruskal 重構樹
- Kruskal重構樹
- 【JAVA演算法】圖論演算法 --求最小生成樹Prim演算法Java演算法圖論
- Prim 最小生成樹 圖解圖解
- BZOJ 1626 [Usaco2007 Dec]Building Roads 修建道路:kruskal(最小生成樹)UI
- Note - kruskal 重構樹
- 最小生成樹學習筆記筆記