數據結構與算法分析–Minimum Spanning Tree(最小生成樹)


給定一個無向圖,如果他的某個子圖中,任意兩個頂點都能互相連通並且是一棵樹,那么這棵樹就叫做生成樹(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: }


免責聲明!

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



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