1、Kruskal算法描述
Kruskal算法是基於貪心的思想得到的。首先我們把所有的邊按照權值先從小到大排列,接着按照順序選取每條邊,如果這條邊的兩個端點不屬於同一集合,那么就將它們合並,直到所有的點都屬於同一個集合為止。至於怎么合並到一個集合,那么這里我們就可以用到一個工具——-並查集(不知道的同學請移步:Here)。換而言之,Kruskal算法就是基於並查集的貪心算法。
Prim算法適用於稠密圖 Kruskal適用於稀疏圖
2、Kruskal算法流程
對於圖G(V,E),以下是算法描述:
輸入: 圖G
輸出: 圖G的最小生成樹
具體流程:
(1)將圖G看做一個森林,每個頂點為一棵獨立的樹
(2)將所有的邊加入集合S,即一開始S = E
(3)從S中拿出一條最短的邊(u,v),如果(u,v)不在同一棵樹內,則連接u,v合並這兩棵樹,同時將(u,v)加入生成樹的邊集E'
(4)重復(3)直到所有點屬於同一棵樹,邊集E'就是一棵最小生成樹
輸入: 圖G
輸出: 圖G的最小生成樹
具體流程:
(1)將圖G看做一個森林,每個頂點為一棵獨立的樹
(2)將所有的邊加入集合S,即一開始S = E
(3)從S中拿出一條最短的邊(u,v),如果(u,v)不在同一棵樹內,則連接u,v合並這兩棵樹,同時將(u,v)加入生成樹的邊集E'
(4)重復(3)直到所有點屬於同一棵樹,邊集E'就是一棵最小生成樹
我們用現在來模擬一下Kruskal算法,下面給出一個無向圖B,我們使用Kruskal來找無向圖B的最小生成樹。
首先,我們將所有的邊都進行從小到大的排序。排序之后根據貪心准則,我們選取最小邊(A,D)。我們發現頂點A,D不在一棵樹上,所以合並頂點A,D所在的樹,並將邊(A,D)加入邊集E‘。
我們接着在剩下的邊中查找權值最小的邊,於是我們找到的(C,E)。我們可以發現,頂點C,E仍然不在一棵樹上,所以我們合並頂點C,E所在的樹,並將邊(C,E)加入邊集E'
不斷重復上述的過程,於是我們就找到了無向圖B的最小生成樹,如下圖所示:
3、Kruskal算法的時間復雜度
Kruskal算法每次要從都要從剩余的邊中選取一個最小的邊。通常我們要先對邊按權值從小到大排序,這一步的時間復雜度為為O(|Elog|E|)。Kruskal算法的實現通常使用並查集,來快速判斷兩個頂點是否屬於同一個集合。最壞的情況可能要枚舉完所有的邊,此時要循環|E|次,所以這一步的時間復雜度為O(|E|α(V)),其中α為Ackermann函數,其增長非常慢,我們可以視為常數。所以Kruskal算法的時間復雜度為O(|Elog|E|)。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #include<stack> 8 #include<map> 9 #include<sstream> 10 using namespace std; 11 typedef long long ll; 12 const int maxn = 3e5 + 10; 13 const int INF = 1 << 30; 14 int dir[4][2] = {1,0,0,1,-1,0,0,-1}; 15 int T, n, m, x; 16 struct edge 17 { 18 int u, v, w; 19 bool operator <(const edge& a)const 20 { 21 return w < a.w; 22 } 23 }; 24 edge a[maxn]; 25 int par[600], high[600]; 26 //初始化n個元素 27 void init(int n) 28 { 29 for(int i = 0; i < n; i++) 30 { 31 par[i] = i; 32 high[i] = 0; 33 } 34 } 35 //查詢樹的根 36 int Find(int x) 37 { 38 return par[x] == x ? x : par[x] = Find(par[x]);//路徑壓縮 39 } 40 void unite(int x, int y) 41 { 42 x = Find(x); 43 y = Find(y); 44 if(x == y)return; 45 if(high[x] < high[y])par[x] = y;//y的高度高,將x的父節點設置成y 46 else 47 { 48 par[y] = x; 49 if(high[x] == high[y])high[x]++; 50 } 51 } 52 bool same(int x, int y) 53 { 54 return Find(x) == Find(y); 55 } 56 void kruskal(int n, int m)//點數n,邊數m 57 { 58 int sum_mst = 0;//mst權值 59 int num= 0;//已經選擇的邊的邊數 60 sort(a, a + m);//邊進行排序 61 init(n);//初始化並查集 62 for(int i = 0; i < m; i++) 63 { 64 int u = a[i].u; 65 int v = a[i].v; 66 if(Find(u - 1) != Find(v - 1))//圖最開始的下標是1,並查集是0 67 { 68 printf("%d %d %d\n", u, v, a[i].w); 69 sum_mst += a[i].w; 70 num++; 71 unite(u - 1, v - 1); 72 } 73 if(num >= n - 1)break; 74 } 75 printf("weight of mst is %d\n", sum_mst); 76 } 77 int main() 78 { 79 cin >> n >> m; 80 for(int i = 0; i < m; i++) 81 { 82 cin >> a[i].u >> a[i].v >> a[i].w; 83 } 84 kruskal(n, m); 85 return 0; 86 } 87 輸入: 88 7 9 89 1 2 28 90 1 6 10 91 2 3 16 92 2 7 14 93 3 4 12 94 4 5 22 95 4 7 18 96 5 6 25 97 5 7 24 98 輸出: 99 1 6 10 100 3 4 12 101 2 7 14 102 2 3 16 103 4 5 22 104 5 6 25 105 weight of mst is 99