圖——圖的Kruskal法最小生成樹實現

1,最小生成樹的特徵:ios

 

       1,選取的邊是圖中權值較小的邊;算法

       2,全部邊鏈接後不構成迴路;數組

      

2,prim 算法是以頂點爲核心的,最下生成樹最大的特徵是邊,但 prim 算法非要以頂點爲核心來進行,有些複雜和難以理解;函數

 

3,既然最小生成樹關心的是如何選擇 n - 1 條邊,那麼是否能夠直接以邊爲核心進行算法設計?測試

 

4,簡單嘗試:spa

 

       1,由 4 個頂點構成的圖,選擇 3 條權值最小的邊;設計

       2,還要設法避免迴路;3d

      

 

5,須要解決的問題:code

 

       1,如何判斷新選擇的邊與已選擇的邊是否構成迴路?blog

      

6,技巧:前驅標記數組(避開新加入邊形成迴路問題)

       1,定義數組:Array<int> p(vCount());

       2,定義數組元素的意義:

              1,p[n] 表示頂點 n 在邊的鏈接通路上的另外一端頂點;

       3,前驅標記數組到底是怎麼來的?

      

7,最小生成樹算法的核心步驟(Kruskal):

       1,定義前驅標記數組:Array<int> p(vCount());

       2,獲取當前圖中的全部邊,並存儲於 edges 數組中;

       3,對數組 deges 按照權值進行排序;

       4,利用 p 數組在 edges 數組中選擇前 n - 1 不構成迴路的邊;

      

8,Kruskal 算法流程:

 

      

9,關鍵的 find 查找函數:

     

 

10,最小生成樹算法 Kruskal (克魯斯卡)實現:

 1    /* 最小、大生成樹的 kruskal 算法 */
 2     SharedPointer< Array< Edge<E> > > kruskal(const bool MINMUM = true)
 3     {
 4         LinkQueue< Edge<E> > ret;  // 返回的隊列
 5         SharedPointer< Array< Edge<E> > > edges = getUndirectedEdges();  // 將無相圖中的全部邊都拿到
 6         DynamicArray<int> p(vCount());  // 前驅標記數組
 7 
 8         /* 設置前驅標記值 */
 9         for(int i=0; i<p.length(); i++)
10         {
11             p[i] = -1;
12         }
13 
14         /* 對邊數組排序 */
15         Sort::Shell(*edges, MINMUM);  // 第二個參數對邊進行從大到小的次序排序,用來生成最大生成樹
16 
17         /* 進入循環,挑選邊 */
18         for(int i=0; (i<edges->length()) && (ret.length() < (vCount()-1)); i++)  // 最多循環邊的個數次,且若是邊不少但已經有 N - 1 條邊被選擇了,那麼結束循環                                           
19 {
20             int b = find(p, (*edges)[i].b);  // 在前驅標記數組中查找挑選的邊的兩個頂點
21             int e = find(p, (*edges)[i].e);  // 前驅標記數組用於判斷新選擇的邊是否會形成迴路
22 
23             if( b != e)  // 相等會構成迴路
24             {
25                 p[e] = b;  // 修改前驅標記數組
26 
27                 ret.add((*edges)[i]);  // 將這條邊加入結果集合中去
28             }
29         }
30 
31         if( ret.length() != (vCount()-1) )  // 判斷邊是否夠,不夠就不能構成最小生成樹
32         {
33             THROW_EXCEPTION(InvalidOperationException, "No enough edges for Kruskal operation ...");
34         }
35 
36         return toArray(ret);  // 將結果轉換爲數組
37     }

 

11,Kruskal 算法測試代碼:

 1 #include <iostream>
 2 #include "MatrixGraph.h"
 3 #include "ListGraph.h"
 4 
 5 using namespace std;
 6 using namespace DTLib;
 7 
 8 template< typename V, typename E >
 9 Graph<V, E>& GraphEasy()
10 {
11    static MatrixGraph<4, V, E> g;
12 
13     g.setEdge(0, 1, 1);
14     g.setEdge(1, 0, 1);
15     g.setEdge(0, 2, 3);
16     g.setEdge(2, 0, 3);
17     g.setEdge(1, 2, 1);
18     g.setEdge(2, 1, 1);
19     g.setEdge(1, 3, 4);
20     g.setEdge(3, 1, 4);
21     g.setEdge(2, 3, 1);
22    g.setEdge(3, 2, 1);
23 
24     return g;
25 }
26 
27 template< typename V, typename E >
28 Graph<V, E>& GraphComplex()
29 {
30    static ListGraph<V, E> g(9);
31 
32     g.setEdge(0, 1, 10);
33     g.setEdge(1, 0, 10);
34     g.setEdge(0, 5, 11);
35     g.setEdge(5, 0, 11);
36     g.setEdge(1, 2, 18);
37     g.setEdge(2, 1, 18);
38     g.setEdge(1, 8, 12);
39     g.setEdge(8, 1, 12);
40     g.setEdge(1, 6, 16);
41     g.setEdge(6, 1, 16);
42     g.setEdge(2, 3, 22);
43     g.setEdge(3, 2, 22);
44     g.setEdge(2, 8, 8);
45     g.setEdge(8, 2, 8);
46     g.setEdge(3, 8, 21);
47     g.setEdge(8, 3, 21);
48     g.setEdge(3, 6, 24);
49     g.setEdge(6, 3, 24);
50     g.setEdge(3, 7, 16);
51     g.setEdge(7, 3, 16);
52     g.setEdge(3, 4, 20);
53     g.setEdge(4, 3, 20);
54     g.setEdge(4, 5, 26);
55     g.setEdge(5, 4, 26);
56     g.setEdge(4, 7, 7);
57     g.setEdge(7, 4, 7);
58     g.setEdge(5, 6, 17);
59     g.setEdge(6, 5, 17);
60     g.setEdge(6, 7, 19);
61    g.setEdge(7, 6, 19);
62 
63     return g;
64 }
65 
66 int main()
67 {
68     Graph<int, int>& g = GraphEasy<int, int>();
69    SharedPointer< Array< Edge<int> > > sa = g.kruskal(65535);  
70 
71    int w = 0;
72 
73     for(int i=0; i<sa->length(); i++)
74     {
75         w += (*sa)[i].data;
76 
77         cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl;
78    }
79 
80    cout << "Weight: " << w << endl;
81 
82     return 0;
83 }

 

13,小結:

       1,Prim 算法以頂點爲核心尋找最小生成樹,不夠直接;

       2,Kruskal 算法以邊爲核心尋找最小生成樹,直觀簡單;

       3,Kruskal 算法中的關鍵是前驅標記數組的使用;

       4,前驅標記數組用於判斷新選擇的邊是否會形成迴路;

相關文章
相關標籤/搜索