Kruskal演算法介紹與實現
阿新 • • 發佈:2019-02-08
最小生成樹(MinimumSpanning Tree,MST)或者稱為最小代價生成樹:對無向連通圖的生成樹,各邊的全值總和稱為生成樹的權,權最小的生成樹稱為最小生成樹。
構造最小生成樹的準則有三條:
(1)必須只使用該網路中的邊來構造最小生成樹;
(2)必須使用且僅使用n-1條邊來連線網路中的n個頂點;
(3)不能使用產生迴路的邊。
構造最小生成樹的演算法主要有:克魯斯卡爾(Kruskal)演算法、Boruvka演算法、和普里姆(Prim)演算法,他們均遵循以上準則,且都採用了一種逐步求解的策略。
Kruskal演算法的基本思想是以邊為主導地位,始終都是選擇當前可用的最小權值的邊,步驟如下:
(1)設一個有n個頂點的連通網路為G(V,E),最初先構造一個只有n個頂點,沒有邊的非連通圖T{V,Ø},圖中每個頂點自成一個連通分量;
(2)當在E中選擇一條具有最小權值的邊時,若該邊的兩個頂點落在不同的連通分量上,則將此邊加入到T中;否則,即這條邊的兩個頂點落在同一個連通分量上,則將此邊捨去(此後永不選用這條邊),重新選擇一條權值最小的邊;
(3)如此重複下去,直到所有頂點在同一個連通分量上為止。
Kruskal演算法實現:
以下圖無向網為例:
利用Kruskal演算法求上圖所示的無向網的最小生成樹,並輸出依次選擇的各條邊及最終求得的最小生成樹的權。資料輸入格式:第一行為頂點個數n和邊數 m,第二行開始為每條邊的資料:u,v,w,分別表示這條邊的兩個頂點及邊上的權值。頂點序號從1開始計起。
解:Code:
執行結果:#include<stdio.h> #include<stdlib.h> #include<iostream> using namespace std; #define MAXN 11 //頂點個數的最大值 #define MAXM 20 //邊的個數的最大值 struct edge //邊 { int u, v, w; }edges[MAXM]; //邊的陣列 int parent[MAXN]; //parent[i]為頂點i所在集合對應的樹中的根結點 int n, m; //頂點個數、邊的個數 int i, j; //迴圈變數 void UFset() //初始化 { for(i = 1; i <= n; i++) parent[i] = -1; } int Find(int x) //查詢並返回結點x所屬集合的根結點 { int s; //查詢位置 for(s = x; parent[s] >=0; s = parent[s]) ; while(s != x) //優化方案——壓縮路徑,使後續的查詢操作加速 { int tmp = parent[x]; parent[x] = s; x = tmp; } return s; } //運用並查集,將兩個不同集合的元素進行合併,使兩個集合中任意兩個元素都連通 void Union(int R1, int R2) { int r1 = Find(R1), r2 = Find(R2); //r1和r2分別為R1和R2的根結點 int tmp = parent[r1] + parent[r2]; //兩個集合結點數之和(負數) //如果R2所在樹結點個數 > R1所在樹結點個數(注意parent[r1]是負數) if(parent[r1] > parent[r2]) { parent[r1] = r2; parent[r2] = tmp; } else { parent[r2] = r1; parent[r1] = tmp; } } int cmp(const void *a, const void *b) //實現從小到大的比較函式 { edge aa = *(const edge *)a, bb = *(const edge *)b; return aa.w-bb.w; } void Kruskal() { int sumweight = 0; //生成樹的權值 int num = 0; //已選用的邊的數目 UFset(); //初始化parent陣列 for(i = 0; i < m; i++) { if(Find(edges[i].u) != Find(edges[i].v)) { printf("%d %d %d\n", edges[i].u, edges[i].v, edges[i].w); sumweight += edges[i].w; num++; Union(edges[i].u, edges[i].v); } if(num >= n-1) break; } printf("The weight of MST is : %d\n", sumweight); } void main() { scanf("%d%d", &n, &m); //讀入頂點個數和邊數 for(int i = 0; i < m; i++) scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].w); //讀入邊的起點和終點 printf("The edges chosen are :\n"); qsort(edges, m, sizeof(edges[0]), cmp); //對邊按權值從小到大排序 Kruskal(); }