最小生成樹Kruskal算法(鄰接矩陣和鄰接表)


  最小生成樹,克魯斯卡爾算法.

算法簡述:

  將每個頂點看成一個圖.

  在所有圖中找權值最小的邊.將這條邊的兩個圖連成一個圖,

  重復上一步.直到只剩一個圖.

注:將abcdef每個頂點看成一個圖.將最小權值的邊的兩個圖連接.

連接最小權值為1的兩個圖,這時a-c,b,d,e,f.

連接最小權值為2的兩個圖,這時a-c,b,d-f,e.

連接最小權值為3的兩個圖,這時a-c,b-e,d-f.

連接最小權值為4的兩個圖,這時a-c-f-d,b-e.(c-f)

連接最小權值為5的兩個圖,這時a-c-b-e-f-d.(b-c)

結束.

根據上述操作,我們需要一個存儲邊信息的數組(Edge結構體),Edge包含了邊的兩個節點和權值.

1 typedef struct
2 {
3     int head;//邊的始點下標
4     int tail;//邊的終點下標
5     int power;//邊的權值
6 } Edge;

還需要一個visited數組,用來標識圖中的節點信息.

 

算法操作:

  初始化一棵樹(用來保存最小生成樹,直接輸出也行.)

  將圖中所有邊復制到一個數組中,將數組排序(遞增順序)

  將小邊的兩個頂點連接.將兩個圖合並成一個圖.

  重復上一步.

臨街矩陣的代碼實現

代碼中,ijk做循環用,v1,v2做邊的兩個頂點信息的下標,vs1,vs2做標識v1和v2所屬圖

1-27行,初始化visited,edge,kruskal_tree等信息.

29-44行,生成一棵最小生成樹.

35行,if是為了防止回路,vs1和vs2標識一個這兩點是否屬於一個圖.

38行,for是為了將visited數組中vs2邊成vs1,因為這時,v1和v2已經在一個圖里了.

 1 void kruskal(Graph * graph, Graph * kruskal_tree)
 2 {
 3     int visited[graph->vertexs];
 4     Edge edge[graph->brim];
 5     int i, j, k;
 6     int v1, v2, vs1, vs2;
 7 
 8     for ( i = 0; i < graph->vertexs; i++ )
 9         visited[i] = i;
10 
11     k = 0;
12     for ( i = 0; i < graph->vertexs; i++ )
13     {
14         for ( j = i + 1; j < graph->vertexs; j++ )
15         {
16             if ( graph->arcs[i][j] != MAX_VALUE )
17             {
18                 edge[k].head = i;
19                 edge[k].tail = j;
20                 edge[k].power = graph->arcs[i][j];
21                 k++;
22             }
23         }
24     }
25 
26     init_kruskal(graph, kruskal_tree);
27     my_sort(edge, graph->brim);
28 
29     for ( i = 0; i < graph->brim; i++ )
30     {
31         v1 = edge[i].head;
32         v2 = edge[i].tail;
33         vs1 = visited[v1];
34         vs2 = visited[v2];
35         if ( vs1  != vs2 )
36         {
37             kruskal_tree->arcs[v1][v2] = graph->arcs[v1][v2];
38             for ( j = 0; j < graph->vertexs; j++ )
39             {
40                 if ( visited[j] == vs2 )
41                     visited[j] = vs1;
42             }
43         }
44     }
45 }

臨街矩陣源碼:http://www.cnblogs.com/ITgaozy/p/5200637.html

鄰接表的代碼實現

17行,if是為了將防止邊的重復輸入(在鄰接矩陣中,點在矩陣中是對稱的,所以我們只輸入一個上三角中的數據就夠了.但在鄰接表中,我們如何判斷一條邊是否已經輸入過了? 我的方法是將比當前節點下標大的輸入,例如右a,b兩個節點,a的節點小與b,我們在輸入b的信息時,由於a的節點下標比b小,不輸入a-b這條邊,因為我們在輸入a的信息時,a-b這條邊已經輸入過了.

 1 void kruskal(Graph * graph, Graph * kruskal_tree)
 2 {
 3     int visited[graph->vertexs];
 4     int i, j;
 5     Edge edge[graph->brim];
 6     int v1, v2, vs1, vs2;
 7     Arc_node * cur, * tmp;
 8 
 9     for ( i = 0; i < graph->vertexs; i++ )
10         visited[i] = i;
11 
12     for ( i = 0, j = 0; i < graph->vertexs; i++ )
13     {
14         cur = graph->adjlist[i].next;
15         while ( cur != NULL )
16         {
17             if ( cur->pos > i )
18             {
19                 edge[j].head = i;
20                 edge[j].tail = cur->pos;
21                 edge[j].power = cur->distance;
22                 j++;
23             }
24             cur = cur->next;
25         }
26     }
27 
28     init_kruskal(graph, kruskal_tree);
29     my_sort(edge, graph->brim);
30 
31     for ( i = 0; i < graph->brim; i += 1 )
32     {
33         v1 = edge[i].head;
34         v2 = edge[i].tail;
35         vs1 = visited[v1];
36         vs2 = visited[v2];
37         if ( vs1 != vs2 )
38         {
39             if ( kruskal_tree->adjlist[v1].next == NULL )
40             {
41                 kruskal_tree->adjlist[v1].next = make_node(v2, edge[i].power);
42             }
43             else
44             {
45                 tmp = kruskal_tree->adjlist[v1].next;
46                 while ( tmp->next != NULL )
47                     tmp = tmp->next;
48                 tmp->next = make_node(v2, edge[i].power);
49             }
50             for ( j = 0; j < graph->vertexs; j++ )
51             {
52                 if ( visited[j] == vs2 )
53                     visited[j] = vs1;
54             }
55         }
56     }
57 }

 鄰接表源碼:http://www.cnblogs.com/ITgaozy/p/5200643.html


免責聲明!

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



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