最小生成樹兩個經典算法(Prime算法、Kruskal算法) - biaobiao88

 經典的最小生成樹例子,Prime算法,具體的步驟及其註釋本人均在代碼中附加,請仔細閱讀與品味,要求,能夠熟練的打出。ios

 1 //Prime算法基礎 
 2 #include<iostream>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int n,m,i,j,k,min,t1,t2,t3;
 8     int e[7][7],dis[7],book[7] = {0};
 9     int inf = 99999999;
10     int count = 0,sum = 0;
11     cin >> n >> m;
12     
13     //初始化 用鄰接矩陣存儲 
14     for(int i = 1;i <= n;i++)
15         for(int j = 1;j <= n;j++)
16             if(i == j)
17                 e[i][j] = 0;
18             else
19                 e[i][j] = inf;
20     
21     //讀入邊 無向圖來回都得設置權值 
22     for(int i = 1;i <= m;i++)
23     {
24         cin >> t1 >> t2 >> t3;
25         e[t1][t2] = t3;
26         e[t2][t1] = t3;
27     }
28     
29     //初始化dis數組,這裏是第一個頂點到各個頂點的初始距離,由於當前生成樹中只有一個頂點
30     for(int i = 1;i <= n;i++)
31         dis[i] = e[1][i];
32         
33     //Prime核心部分
34     //將第一個頂點(即1號頂點)加入生成樹
35     book[1] = 1;//這裏用book數組來標記一個頂點是否已經加入生成樹 
36     count++;//表示生成樹中已加入一個頂點 
37     while(count < n)
38     {
39         min = inf;
40         
41         //掃描找出距離當前根頂點相鏈接的最小權值的頂點 
42         for(int i = 1;i <= n;i++)
43         {
44             if(book[i] == 0 && min > dis[i])//若是根節點未被標記而且根節點到剩下的n-1個頂點之間有直連線(就是相連着的邊) 
45             {
46                 min = dis[i];//就將這條邊的權值代替以前初始化時的無窮大 ,更新min的值,min的值更新只在此for循環中有效,此for循環目的是找出鏈接根節點權值最小的那個頂點j,再把min放入for循環中,一直比較,直至找到最小的權值及連的頂點j 
47                 j = i;//同時將此時的頂點的編號記錄在臨時變量j中,以便接下來使用 
48             }
49         }
50         book[j] = 1;//將上面for循環中找到的最小權值的頂點j加入到生成樹中並標記 
51         count++;//生成樹中的頂點加一 
52         sum += dis[j];//將權值最小的邊都累加,最終獲得最優解 
53         
54         //掃描當前頂點j全部的邊,再以j爲中心點,更新生成樹到每個非樹頂點的距離 
55         for(k = 1;k <= n;k++)
56         {
57             if(book[k] == 0 && dis[k] > e[j][k])//e[k]表示第一個加入的根頂點到頂點k之間的權值(可能直接鏈接也可能間接鏈接),e[j][k]表示的是當前根頂點做爲j,到與j頂點直接相連的頂點k的權值 
58                 dis[k] = e[j][k];//此循環的目的是找出離j頂點最近的頂點 
59         }
60     }
61     cout << sum;
62     return 0;
63 }
64 /*
65 6 9
66 2 4 11
67 3 5 13
68 4 6 3
69 5 6 4
70 2 3 6
71 4 5 7
72 1 2 1
73 3 4 9
74 1 3 2
75 19
76 */

 運行結果:算法

 

 Kruskal算法,須要用到並查集,具體請看如下代碼數組

  1 //Kruskal算法 
  2 #include<iostream>
  3 #include<algorithm>
  4 using namespace std;
  5 struct edge
  6 {
  7     int u;
  8     int v;
  9     int w;
 10 }e[10];//爲了方便排序,這裏建立了一個結構體數組來存儲邊的關係,數組大小根據實際狀況來設置,要比m的最大值大1 
 11 int n,m;
 12 int f[7],sum = 0,countt = 0;//並查集須要用到的一些變量,f數組大小根據實際狀況來設置,要比n的最大值大1 
 13 
 14 ////這個也是快速排序,也能夠替代sort函數 
 15 //void quicksort(int left,int right)
 16 //{
 17 //    int i,j;
 18 //    struct edge t;
 19 //    if(left > right)
 20 //        return;
 21 //    i = left;
 22 //    j = right;
 23 //    while(i != j)
 24 //    {
 25 //        //順序很重要,要先從右邊開始找 
 26 //        while(e[j].w >= e[left].w && i < j)
 27 //            j--;
 28 //        //再從左邊開始找 
 29 //        while(e[i].w <= e[left].w && i < j)
 30 //            i++;
 31 //        //交換 
 32 //        if(i < j)
 33 //        {
 34 //            t = e[i];
 35 //            e[i] = e[j];
 36 //            e[j] = t;
 37 //        }
 38 //    }
 39 //    //最終將基準數歸位,將left和i互換 
 40 //    t = e[left];
 41 //    e[left] = e[i];
 42 //    e[i] = t;
 43 //    
 44 //    quicksort(left,i - 1);//繼續處理左邊的,這裏是一個遞歸的過程 
 45 //    quicksort(i + 1,right);//繼續處理右邊的,這裏是一個遞歸的過程 
 46 //    return;
 47 //}
 48 
 49 //並查集尋找祖先的函數 
 50 int getf(int v)
 51 {
 52     if(f[v] == v)
 53         return v;
 54     else
 55     {
 56         //這裏是路徑壓縮 
 57         f[v] = getf(f[v]);
 58         return f[v];
 59     }
 60 }
 61 //並查集合並兩個子集合的函數 
 62 int merge(int v,int u)
 63 {
 64     int t1,t2;
 65     t1 = getf(v);
 66     t2 = getf(u);
 67     if(t1 != t2)//判斷兩個點是否在同一個集合中 
 68     {
 69         f[t2] = t1;
 70         return 1;
 71     }
 72     return 0;
 73 }
 74 
 75 bool cmp(const edge &a,const edge &b)
 76 {
 77     return a.w < b.w;
 78 }
 79 
 80 int main()
 81 {
 82     //讀入頂點個數n和邊的條數m 
 83     cin >> n >> m;
 84     //讀入邊,這裏用結構體數組來存儲邊的關係 
 85     for(int i = 1;i <= m;i++)
 86         cin >> e[i].u >> e[i].v >> e[i].w;
 87 //    quicksort(1,m);//按照權值從大到小對邊進行快速排序
 88     //快速排序權值 
 89     sort(e + 1,e + m + 1,cmp);
 90     //並查集初始化
 91     for(int i = 1;i <= n;i++)
 92         f[i] = i;
 93     
 94     //Kruskal算法核心部分
 95     for(int i = 1;i <= m;i++)
 96     {
 97         //判斷一條邊的兩個頂點是否已經連通,即判斷是否已在同一個集合中 
 98         if(merge(e[i].u,e[i].v))//若是目前不連通,則選用這條邊 
 99         {
100             countt++;//知足條件將頂點加入生成樹 
101             sum += e[i].w;//路徑累加和 
102         }
103         if(countt == n - 1)//當頂點均加入到生成樹中時,結束循環 
104             break;
105     }
106     cout << sum << endl;
107     return 0;
108 }

運行結果:函數

 

 並查集例題ui

下面是對並查集使用的代碼,並查集不太理解的能夠看看下面的代碼spa

 1 //並查集的使用 
 2 #include<iostream>
 3 using namespace std;
 4 int f[1000],n,m,k,sum;
 5 
 6 //這是找爹的遞歸函數,不停的去找爹,直到找到祖宗爲止,即找到根節點,這個函數的目的是判斷兩個頂點是否屬於同一個集合 
 7 int getf(int v)
 8 {
 9     if(f[v] == v)
10         return v;
11     else
12     {
13         /*這裏是路徑壓縮,每次在函數返回的時候,順帶把同一個集合裏面的元素都改成根節點的祖先編號,這樣能夠提升從此找到樹的祖先的速度*/
14         f[v] = getf(f[v]);
15         return f[v];
16     }
17 }
18 //這裏是合併兩子集合的函數,在合併函數中調用getf函數,看兩個頂點是不是屬於同一個集合,而後再決定是否執行合併操做 
19 void merge(int v,int u)
20 {
21     int t1,t2;
22     t1 = getf(v);
23     t2 = getf(u);
24     if(t1 != t2)//根據調用getf的函數,判斷兩個頂點是否在同一個集合中,便是否爲同一個祖先 
25     {
26         f[t2] = t1;
27     }
28 }
29 
30 int main()
31 {
32     int x,y;
33     cin >> n >> m;
34     //這裏是初始化,很是的重要,數組裏面存的是本身數組下標的編號就行了
35     for(int i = 1;i <= n;i++)
36         f[i] = i;
37     for(int i = 1;i <= m;i++)
38     {
39         cin >> x >> y;
40         merge(x,y);//將兩個頂點傳入合併集合函數中進行操做 
41     }
42     for(int i = 1;i <= n;i++)
43     {
44         if(f[i] == i)//根節點的值依舊是原先的值,即判斷有幾個頂點的值不變即有幾個不一樣的集合 
45             sum++;
46     }
47     cout << sum << endl;
48     return 0;
49 }
50 /*
51 10 9
52 1 2
53 3 4
54 5 2
55 4 6
56 2 6
57 8 7
58 9 7
59 1 6
60 2 4
61 3
62 */

運行結果:3d

 

code

相關文章
相關標籤/搜索