1. 程式人生 > >圖的算法專題——最小生成樹

圖的算法專題——最小生成樹

流程 class 最短 端點 優化 等於 d+ size truct

概要:

  1. Prim算法
  2. Kruskal算法

1、Prim算法

算法流程:

(1)對圖G(V,E)設置集合S來存放已被並入的頂點,然後執行n次(2)(3)

(2)每次從未並入頂點集合中選擇與集合S最近的一個頂點,訪問u並將其加入集合S,同時把這條離集合S最近的邊加入最小生成樹中。

(3)優化松弛

Prim算法與Dijkstra算法使用的思想幾乎完全相同,只有在數組d[]的含義上有區別。(關於Dijkstra算法具體可以看前面寫的《圖的算法專題——最短路徑》)

Dijkstra中d[]含義為起點s到達頂點Vi的最短距離,而Prim算法d[]含義為頂點Vi與集合S的最短距離,兩者區別僅在於最短距離是到 起點 還是 集合 。

鄰接矩陣版:

 1 const int maxn=1000;
 2 const int INF=1000000000;
 3 int n,G[maxn][maxn];
 4 int d[maxm]; //頂點到集合S的距離
 5 bool vis[maxn]={false};
 6 
 7 int prim(){ //默認0號為初始點,函數返回最小生成樹的邊權之和
 8     fill(d,d+maxn,INF);
 9     d[0]=0;
10     int ans=0; //邊權之和
11     for(int i=0;i<n;i++){
12         int u=-1,MIN=INF;
13         for
(int j=0;j<n;j++){ 14 if(vis[j]==false&&d[j]<MIN){ 15 u=j; 16 MIN=d[j]; 17 } 18 } 19 if(u==-1) return -1; 20 vis[u]=true; 21 ans+=d[u]; //將與集合S距離最小的邊加入最小生成樹 22 for(int v=0;v<n;v++){ 23 if
(vis[v]==false && G[u][v]!=INF && G[u][v]<d[v] ){ 24 d[v]=G[u][v]; //以u為中介點可以使v離集合S更近(u已被並入集合) 25 } 26 } 27 } 28 return ans; 29 }

鄰接表版:

 1 struct Node{
 2     int v,dis;  //v為目標頂點,dis為邊權
 3 };
 4 vector<Node> Adj[maxn];
 5 int n,d[maxn];
 6 bool vis[maxn]={false};
 7 int prim(){
 8     fill(d,d+maxn,INF);
 9     d[0]=0;
10     int ans=0;
11     for(int i=0;i<n;i++){
12         int u=-1,MIN=INF;
13         for(int j=0;j<n;j++){
14             if(vis[j]==false && d[j]<MIN){
15                 u=j;
16                 MIN=d[j];
17             }
18         }
19         if(u==-1) return -1;
20         vis[u]=true;
21         ans+=d[u];
22         for(int j=0;j<Adj[u].size();j++){
23             int v=Adj[u][j].v;
24             if(vis[v]==false && Adj[u][j].dis< d[v]){
25                 d[v]=Adj[u][j].dis;
26             }
27         }
28     }
29     return ans;
30 }

2、Kruskal算法

算法流程:

(1)對所有邊權從小到大排序。

(2)按邊權從小到大測試所有邊,如果當前測試邊所連接的兩個頂點不在同一個連通塊中,則並入,否則舍棄。 (運用並查集)

(3)循環執行(2)直到最小生成樹的邊數等於總頂點數減1或是測試完所有邊時結束。若結束時邊數不為頂點數減1,則圖不連通。

 1 const int MAXV=110;
 2 const int MAXE=10010;
 3 int father[MAXV];
 4 int findFather(int x){
 5     int a=x;
 6     while(x!=father[x]){
 7         x=father[x];
 8     }
 9     while(a!=father[a]){
10         int z=a;
11         a=father[a];
12         father[z]=x;
13     }
14     return x;
15 }
16 struct edge{
17     int u,v; //邊的兩個端點編號
18     int cost; //邊權
19 }E[MAXE];
20 bool cmp(edge a,edge b){
21     return a.cost<b.cost;
22 }
23 int kruskal(int n,int m){  //n為頂點個數,m為圖的邊數
24     int ans=0,Num_Edge=0;
25     for(int i=1;i<=n;i++) father[i]=i; //假設頂點範圍為1到n 
26     sort(E,E+m,cmp);
27     for(int i=0;i<m;i++){ //枚舉邊
28         int faU=findFather(E[i].u);
29         int faV=findFather(E[i].v);
30         if(faU!=faV){
31             father[faU]=faV;
32             ans+=E[i].cost;
33             Num_Edge++;
34             if(Num_Edge==n-1) break;
35         }
36     }
37     if(Num_Edge != n-1) return -1;
38     else return ans;
39 }

圖的算法專題——最小生成樹