最小生成樹之kruskal算法


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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM