1. 程式人生 > >圖->連通性->最小生成樹(克魯斯卡爾演算法) 最小生成樹(普里姆演算法)

圖->連通性->最小生成樹(克魯斯卡爾演算法) 最小生成樹(普里姆演算法)

文字描述

  上一篇部落格介紹了最小生成樹(普里姆演算法),知道了普里姆演算法求最小生成樹的時間複雜度為n^2, 就是說複雜度與頂點數無關,而與弧的數量沒有關係;

  而用克魯斯卡爾(Kruskal)演算法求最小生成樹則恰恰相反。它的時間複雜度為eloge (e為網中邊的數目),因此它相對於普里姆演算法而言,適合於求邊稀疏的網的最小生成樹。

  克魯斯卡爾演算法求最小生成樹的步驟為:假設連通網N={V,{E}}, 則令最小生成樹的初始狀態為只有n個頂點而無邊的非連通圖 T=(V, {}}, 圖中每個頂點自成一個連通分量。在E中選擇代價最小的邊,若該邊依附的頂點落在T中不同的連通分量中,則將此邊加入到T中,否則捨去此邊而選擇下一條代價最小的邊。依次類推,直至T中所有頂點都在同一連通分量上為止。

 

示意圖:

 

演算法分析:

  實現克魯斯卡爾的話,可以藉助於之前介紹的"堆"(堆排序)和樹的等價劃分的方法。用”堆”來存放網中的邊,則每次選擇最小代價的邊僅需loge的時間(第一次需e)。又生成樹T的每個連通分量可看成一個等價類,則構造T加入新的邊的過程類似於求等價類的過程,由此可用MFSet型別來描述頂點,用堆Heap存放弧結點資訊,使構造T的過程僅需eloge的時間,由此,克魯斯

 

程式碼實現

  1 //
  2 // Created by lady on 18-12-15.
  3 //
  4 
  5 #include <stdio.h>
  6
#include <stdlib.h> 7 #include <string.h> 8 9 #define MAX_NODE_NUM 20 10 #define MAX_ARCH_NUM 30 11 12 #define EQ(a, b) ((a)==(b)) 13 #define LT(a, b) ((a) <(b)) 14 #define LQ(a, b) ((a)<=(b)) 15 16 17 typedef struct PTNode{ 18 char data[10]; 19 int index;
20 int parent; 21 }PTNode; 22 23 typedef struct{ 24 PTNode node[MAX_NODE_NUM+1]; 25 int n; 26 }MFSet; 27 28 typedef struct ArcBox{ 29 int vex1, vex2; 30 int weight; 31 }ArcBox; 32 33 typedef struct{ 34 ArcBox r[MAX_ARCH_NUM+1]; 35 int len; 36 }HeapType; 37 38 /* param1 S: S是已存在的集合 39 * param2 index: index是S中某個子集的成員的下標值 40 * result: 查詢函式,確定S中位置為index所屬子集Si,並返回其子集中的根結點的位置。 41 */ 42 static int findMFSet(MFSet *S, int index) 43 { 44 if(index < 1 || index > S->n) 45 return -1; 46 int j = 0; 47 for(j=index; S->node[j].parent>0; j=S->node[j].parent); 48 return j; 49 } 50 51 /* 52 * 集合S中位置為index_i和index_j所在的子集互不相交。 53 * result: 將置為index_i和index_j所在的互不相交的子集合併為一個子集。 54 */ 55 static int mergeMFSet(MFSet *S, int index_i, int index_j) 56 { 57 int loc_i = -1, loc_j = -1; 58 if((loc_i=findMFSet(S, index_i)) < 0){ 59 return -1; 60 } 61 if((loc_j=findMFSet(S, index_j)) < 0){ 62 return -1; 63 } 64 if(loc_i == loc_j){ 65 return -1; 66 } 67 //結點數少的子集指向結點數大的子集 68 if(S->node[loc_i].parent > S->node[loc_j].parent){ 69 S->node[loc_j].parent += S->node[loc_i].parent; 70 S->node[loc_i].parent = loc_j; 71 }else{ 72 S->node[loc_i].parent += S->node[loc_j].parent; 73 S->node[loc_j].parent = loc_i; 74 } 75 return 0; 76 } 77 78 static int initialMFSet(MFSet *S, int n) 79 { 80 int i = 0; 81 S->n = n; 82 for(i=1; i<=S->n; i++) 83 { 84 printf("輸入第%d個子集:", i); 85 scanf("%s", S->node[i].data); 86 S->node[i].parent = -1; 87 S->node[i].index = i; 88 } 89 return 0; 90 } 91 92 /* 93 * 返回結點中資料等於data的下標值 94 */ 95 static int getLocaofVex(MFSet *S, char data[]) 96 { 97 int i = 0; 98 for(i=1; i<=S->n; i++){ 99 if(!strncasecmp(S->node[i].data, data, sizeof(S->node[0].data))){ 100 return S->node[i].index; 101 } 102 } 103 return -1; 104 } 105 106 static void printMFSet(MFSet *S) 107 { 108 printf("列印MFSet結構中的資料:\n"); 109 int i = 0; 110 for(i=1; i<=S->n; i++){ 111 printf("index %d:(data %s, parent %d)\n", S->node[i].index, S->node[i].data, S->node[i].parent); 112 } 113 printf("\n"); 114 } 115 116 117 118 ////////////////////////////////////////////// 119 120 static int printHeap(HeapType *H) 121 { 122 printf("列印堆結構中的資料:\n"); 123 int i = 0; 124 for(i=1; i<=H->len; i++){ 125 printf("index %d: arch:(%d,%d),weight %d)\n", i, H->r[i].vex1, H->r[i].vex2, H->r[i].weight); 126 } 127 return 0; 128 } 129 130 static int initialHeap(HeapType *H, MFSet *S, int n) 131 { 132 H->len = n; 133 int i = 0; 134 char s1[10] = {0}; 135 char s2[10] = {0}; 136 char s3[10] = {0}; 137 int weight = 0; 138 139 char tmp[30] = {0}; 140 for(i=1; i<=H->len; i++) 141 { 142 printf("輸入第%d條弧資訊(頂點1 頂點2 權值):", i); 143 memset(tmp, 0, sizeof(tmp)); 144 scanf("%s", tmp); 145 sscanf(tmp, "%[^','],%[^','],%s[^\\n]", s1, s2, s3); 146 H->r[i].vex1 = getLocaofVex(S, s1); 147 H->r[i].vex2 = getLocaofVex(S, s2); 148 H->r[i].weight = atoi(s3); 149 } 150 return 0; 151 } 152 153 /* 154 *已知H->r[s,...,m]中記錄的關鍵字除H->r[s].key之外均滿足的定義 155 *本函式調整H-r[s]的關鍵字,使H->r[s,...,m]成為一個小頂堆(對其中 156 *記錄的弧的權值而言) 157 */ 158 void HeapAdjust(HeapType *H, int s, int m) 159 { 160 ArcBox rc = H->r[s]; 161 int j = 0; 162 //沿weight較小的孩子結點向下篩選 163 for(j=2*s; j<=m; j*=2){ 164 //j為weight較小的孩子結點下標 165 if(j<m && (!LQ(H->r[j].weight, H->r[j+1].weight))) 166 j+=1; 167 if(LQ(rc.weight, H->r[j].weight)) 168 break; 169 H->r[s] = H->r[j]; 170 s=j; 171 } 172 H->r[s] = rc; 173 } 174 175 void HeapSort(HeapType *H, MFSet *S) 176 { 177 int i = 0; 178 printf("1)建立一個小頂堆!\n"); 179 //把H->r[1,...,H->length]建成小頂堆 180 for(i=H->len/2; i>=1; i--){ 181 HeapAdjust(H, i, H->len); 182 } 183 printHeap(H); 184 printf("2)依次輸出堆頂元素並重新調整成小頂堆!\n"); 185 ArcBox tmp; 186 for(i=H->len; i>1; i--){ 187 tmp = H->r[1]; 188 H->r[1] = H->r[i]; 189 H->r[i] = tmp; 190 HeapAdjust(H, 1, i-1); 191 printf("新堆頂的弧資訊: (%d,%d) %d", tmp.vex1, tmp.vex2, tmp.weight); 192 if(mergeMFSet(S, tmp.vex1, tmp.vex2) > -1){ 193 printf("\t是最小生成樹的弧!\n"); 194 }else{ 195 printf("\n"); 196 } 197 } 198 } 199 200 201 202 203 int main(int argc, char *argv[]) 204 { 205 int nodes=0; 206 int archs=0; 207 printf("輸入頂點數和弧數:"); 208 scanf("%d,%d", &nodes, &archs); 209 210 printf("\n以MFSet結構存放頂點資訊:\n"); 211 MFSet S; 212 initialMFSet(&S, nodes); 213 printMFSet(&S); 214 215 printf("以堆結構存放弧資訊:\n"); 216 HeapType H; 217 initialHeap(&H, &S, archs); 218 printHeap(&H); 219 220 printf("對存放了弧資訊的堆進行排序,在排序過程中輸入最小生成樹的邊\n"); 221 HeapSort(&H, &S); 222 return 0; 223 }
最小生成樹(克魯斯卡爾演算法)

 

程式碼執行

/home/lady/CLionProjects/untitled/cmake-build-debug/untitled
輸入頂點數和弧數:6,10

以MFSet結構存放頂點資訊:
輸入第1個子集:V1
輸入第2個子集:V2
輸入第3個子集:V3
輸入第4個子集:V4
輸入第5個子集:V5
輸入第6個子集:V6
列印MFSet結構中的資料:
index 1:(data V1, parent -1)
index 2:(data V2, parent -1)
index 3:(data V3, parent -1)
index 4:(data V4, parent -1)
index 5:(data V5, parent -1)
index 6:(data V6, parent -1)

以堆結構存放弧資訊:
輸入第1條弧資訊(頂點1 頂點2 權值):V3,V1,1
輸入第2條弧資訊(頂點1 頂點2 權值):V3,V2,5
輸入第3條弧資訊(頂點1 頂點2 權值):V3,V5,6
輸入第4條弧資訊(頂點1 頂點2 權值):V3,V6,4
輸入第5條弧資訊(頂點1 頂點2 權值):V3,V4,5
輸入第6條弧資訊(頂點1 頂點2 權值):V1,V2,6
輸入第7條弧資訊(頂點1 頂點2 權值):V2,V5,3
輸入第8條弧資訊(頂點1 頂點2 權值):V5,V6,6
輸入第9條弧資訊(頂點1 頂點2 權值):V6,V4,2
輸入第10條弧資訊(頂點1 頂點2 權值):V4,V1,5
列印堆結構中的資料:
index 1: arch:(3,1),weight 1)
index 2: arch:(3,2),weight 5)
index 3: arch:(3,5),weight 6)
index 4: arch:(3,6),weight 4)
index 5: arch:(3,4),weight 5)
index 6: arch:(1,2),weight 6)
index 7: arch:(2,5),weight 3)
index 8: arch:(5,6),weight 6)
index 9: arch:(6,4),weight 2)
index 10: arch:(4,1),weight 5)
對存放了弧資訊的堆進行排序,在排序過程中輸入最小生成樹的邊
1)建立一個小頂堆!
列印堆結構中的資料:
index 1: arch:(3,1),weight 1)
index 2: arch:(6,4),weight 2)
index 3: arch:(2,5),weight 3)
index 4: arch:(3,6),weight 4)
index 5: arch:(3,4),weight 5)
index 6: arch:(1,2),weight 6)
index 7: arch:(3,5),weight 6)
index 8: arch:(5,6),weight 6)
index 9: arch:(3,2),weight 5)
index 10: arch:(4,1),weight 5)
2)依次輸出堆頂元素並重新調整成小頂堆!
新堆頂的弧資訊: (3,1) 1    是最小生成樹的弧!
新堆頂的弧資訊: (6,4) 2    是最小生成樹的弧!
新堆頂的弧資訊: (2,5) 3    是最小生成樹的弧!
新堆頂的弧資訊: (3,6) 4    是最小生成樹的弧!
新堆頂的弧資訊: (4,1) 5
新堆頂的弧資訊: (3,4) 5
新堆頂的弧資訊: (3,2) 5    是最小生成樹的弧!
新堆頂的弧資訊: (5,6) 6
新堆頂的弧資訊: (3,5) 6

Process finished with exit code 0