給定一個無向圖,如果他的某個子圖中,任意兩個頂點都能互相連通並且是一棵樹,那么這棵樹就叫做生成樹(spanning tree).如果邊上有權值,那么使得邊權和最小的生成樹叫做最小生成樹(MST,Minimum Spanning Tree)。
1.prim版本的算法
1: #include<string.h>
2: #define INF 10000001
3: #define N 10001
4: int graph[N][N]; //夾着我們有N個點,這里存的是邊(i,j)的花費(無向邊)
5: //沒有邊時的花費就是INF
6: int cost[N]; //記錄目前要把第i個點加入正確聯盟所需要的花費
7: int last[N]; //記錄第i個點是透過誰加入了正確聯盟(等於是存在edge(last[i],i))
8: int choosed[N]; //記錄是否已經加入正確聯盟
9: int fin_cnt; //記錄已經加入正確聯盟點的個數
10: int total_cost; //記錄總花費
11: void init(){
12: int i;
13: memset( choosed , 0 , sizeof(int));
14: //last = -1代表自己就是root,一開始所有點都是自己的parent
15: memset( last , -1 , sizeof(int));
16:
17: //以idx=0的點作為root開始看花費
18: cost[0]=0;
19: choosed[0]=1;
20: for( i = 1 ; i < N ; i++ ){
21: cost[i] = graph[0][i]; //如果有邊cost就會是該條邊,反之則會是INF
22: if( cost[i] != INF)
23: last[i] = 0;
24: }
25: fin_cnt=1; //一開始只有一個點在正確聯盟
26: }
27:
28: void prim(){
29: int min; //用來存這一輪找到的最小花費
30: int min_idx; //用來存這一輪找到最小花費的是哪個點
31: int i;
32: while( fin_cnt < N ){ //如果小於N代表還沒找完
33: min = INF; //初始化成INF,用來找最小值
34: min_idx=-1;
35: for( i = 1 ; i < N ; i++ ){ //跑過所有點,找最小值
36: if(choosed[i] == 0&&cost[i]<min){//已經在正確聯盟里就不考慮
37: min_idx=i;
38: min=cost[i];
39: }
40: }
41: if( min_idx == -1 ) //如果沒有找到就代表此圖找不到spanning tree
42: break;
43:
44: choosed[min_idx]=1; //標記min_idx這個點進入了正確聯盟
45: total_cost+=cost[min_idx]; //加上加入這個點的cost
46: fin_cnt++; //fin_cnt增加一,代表多了一個點已經確定
47:
48: //看看還有沒有被選的點,有沒有點能夠透過min_idx這個點而更近的
49: for( i = 1 ; i < N ; i++){
50: if(choosed[min_idx] == 0 && graph[min_idx][i]<cost[i]){ //被選過的就跳過,有更近就更新
51: last[i] = min_idx;
52: cost[i] = graph[min_idx][i];
53: }
54: }
55: }
56: }
2.Kruskal版本的算法
Kruskal算法按照邊的權值從小到大排序,再全部訪問一遍,如果將該邊加入當前生成樹內不會產生圈,那么就把這條邊加入到生成樹中,逐步擴大生成樹的大小。
接下來我們介紹如何判斷是否產生重邊。假設現在要把連接頂點u和頂點v的邊e(u—>v,v—>u)加入到生成樹中去,如果加入操作之前,u和v不在同一個連通分量內(兩塊不連接的圖),那么加入e也不會產生圈。反之,如果u和v在同一個連通分量里,那么一定會產生圈。可以使用並查集搞笑的判斷是否屬於同一個連通分量。
1: #include<stdlib.h> //使用memset需要包含的頭文件
2: #include<stdio.h>
3: #include<string.h>
4: #define maxn 10000
5: #define N 101
6: struct node{
7: int u,v,w;
8: }edges[maxn];
9: int total_cost;
10: int id[N];
11: int choosed[N];
12: int comp(const void*p,const void *q){//qsort需要重寫它的排序規則
13: struct node a=*(struct node *)p;//類型強制轉換
14: struct node b=*(struct node *)q;
15: return a.w-b.w;
16: }
17: int find_root(int idx){
18: if(id[idx]==-1)
19: return idx;
20: return id[idx]=find_root(id[idx]);
21: }
22:
23: void init(int n,int m){
24: int i;
25: memset(choosed,0,sizeof(choosed));
26: qsort(edges,n,sizeof(struct node),comp);//按邊從小到大排序
27:
28: for(i=0;i<=m;i++)
29: id[i]=-1;
30: total_cost=0;
31: }
32: void kruskal(int n){
33: int i,x,y;
34: for(i=0;i<n;i++){
35: x=find_root(edges[i].u);
36: y=find_root(edges[i].v);
37: if(x!=y){//如果該條邊添加后不構成回路
38: id[y]=x;
39: total_cost+=edges[i].w;//加上該條邊的權重
40: choosed[edges[i].u]=1;
41: choosed[edges[i].v]=1;
42: }
43: }
44: }