最小生成樹
在含有n個頂點的連通圖中選擇n-1條邊,構成一棵極小連通子圖,並使該連通子圖中n-1條邊上權值之和達到最小,則稱其為連通網的最小生成樹。 
例如,對於如上圖G4所示的連通網可以有多棵權值總和不相同的生成樹。
克魯斯卡爾算法介紹
克魯斯卡爾(Kruskal)算法,是用來求加權連通圖的最小生成樹的算法。
基本思想:按照權值從小到大的順序選擇n-1條邊,並保證這n-1條邊不構成回路。
具體做法:首先構造一個只含n個頂點的森林,然后依權值從小到大從連通網中選擇邊加入到森林中,並使森林中不產生回路,直至森林變成一棵樹為止。
克魯斯卡爾算法圖解
以上圖G4為例,來對克魯斯卡爾進行演示(假設,用數組R保存最小生成樹結果)。
第1步:將邊<E,F>加入R中。
邊<E,F>的權值最小,因此將它加入到最小生成樹結果R中。
第2步:將邊<C,D>加入R中。
上一步操作之后,邊<C,D>的權值最小,因此將它加入到最小生成樹結果R中。
第3步:將邊<D,E>加入R中。
上一步操作之后,邊<D,E>的權值最小,因此將它加入到最小生成樹結果R中。
第4步:將邊<B,F>加入R中。
上一步操作之后,邊<C,E>的權值最小,但<C,E>會和已有的邊構成回路;因此,跳過邊<C,E>。同理,跳過邊<C,F>。將邊<B,F>加入到最小生成樹結果R中。
第5步:將邊<E,G>加入R中。
上一步操作之后,邊<E,G>的權值最小,因此將它加入到最小生成樹結果R中。
第6步:將邊<A,B>加入R中。
上一步操作之后,邊<F,G>的權值最小,但<F,G>會和已有的邊構成回路;因此,跳過邊<F,G>。同理,跳過邊<B,C>。將邊<A,B>加入到最小生成樹結果R中。
此時,最小生成樹構造完成!它包括的邊依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。
克魯斯卡爾算法分析
根據前面介紹的克魯斯卡爾算法的基本思想和做法,我們能夠了解到,克魯斯卡爾算法重點需要解決的以下兩個問題:
問題一 對圖的所有邊按照權值大小進行排序。
問題二 將邊添加到最小生成樹中時,怎么樣判斷是否形成了回路。
問題一很好解決,采用排序算法進行排序即可。
問題二,處理方式是:記錄頂點在"最小生成樹"中的終點,頂點的終點是"在最小生成樹中與它連通的最大頂點"(關於這一點,后面會通過圖片給出說明)。然后每次需要將一條邊添加到最小生存樹時,判斷該邊的兩個頂點的終點是否重合,重合的話則會構成回路。 以下圖來進行說明:
在將<E,F> <C,D> <D,E>加入到最小生成樹R中之后,這幾條邊的頂點就都有了終點:
(01) C的終點是F。
(02) D的終點是F。
(03) E的終點是F。
(04) F的終點是F。
關於終點,就是將所有頂點按照從小到大的順序排列好之后;某個頂點的終點就是"與它連通的最大頂點"。 因此,接下來,雖然<C,E>是權值最小的邊。但是C和E的重點都是F,即它們的終點相同,因此,將<C,E>加入最小生成樹的話,會形成回路。這就是判斷回路的方式。
克魯斯卡爾算法的代碼說明
有了前面的算法分析之后,下面我們來查看具體代碼。這里選取"鄰接矩陣"進行說明,對於"鄰接表"實現的圖在后面的源碼中會給出相應的源碼。
1. 基本定義
// 鄰接矩陣 typedef struct _graph { char vexs[MAX]; // 頂點集合 int vexnum; // 頂點數 int edgnum; // 邊數 int matrix[MAX][MAX]; // 鄰接矩陣 }Graph, *PGraph; // 邊的結構體 typedef struct _EdgeData { char start; // 邊的起點 char end; // 邊的終點 int weight; // 邊的權重 }EData;
Graph是鄰接矩陣對應的結構體。
vexs用於保存頂點,vexnum是頂點數,edgnum是邊數;matrix則是用於保存矩陣信息的二維數組。例如,matrix[i][j]=1,則表示"頂點i(即vexs[i])"和"頂點j(即vexs[j])"是鄰接點;matrix[i][j]=0,則表示它們不是鄰接點。
EData是鄰接矩陣邊對應的結構體。
2. 克魯斯卡爾算法
#include<stdio.h> #include<stdlib.h> #include<malloc.h> #include<string.h> #define MAX 100 #define INF -1 typedef struct Graph { char vexs[MAX]; int vexnum; int edgnum; int matrix[MAX][MAX]; }Graph,*PGraph; typedef struct EdgeData { char start; char end; int weight; }EData; static int get_position(Graph g,char ch) { int i; for(i=0;i<g.vexnum;i++) if(g.vexs[i]==ch) return i; return -1; } Graph* create_graph() { char vexs[]= {'A','B','C','D','E','F','G'}; int matrix[][7]= { {0,12,INF,INF,INF,16,14}, {12,0,10,INF,INF,7,INF}, {INF,10,0,3,5,6,INF}, {INF,INF,3,0,4,INF,INF}, {INF,INF,5,4,0,INF,8}, {16,7,6,INF,2,0,9}, {14,INF,INF,INF,8,9,0}}; int vlen=sizeof(vexs)/sizeof(vexs[0]); int i,j; Graph *pG; if((pG=(Graph*)malloc(sizeof(Graph)))==NULL) return NULL; memset(pG,0,sizeof(pG)); pG->vexnum=vlen; for(i=0;i<pG->vexnum;i++) pG->vexs[i]=vexs[i]; for(i=0;i<pG->vexnum;i++) for(j=0;j<pG->vexnum;j++) pG->matrix[i][j]=matrix[i][j]; for(i=0;i<pG->vexnum;i++) { for(j=0;j<pG->vexnum;j++) { if(i!=j&&pG->matrix[i][j]!=INF) pG->edgnum++; } } pG->edgnum/=2; return pG; } void print_graph(Graph G) { int i,j; printf("Matrix Graph: \n"); for(i=0;i<G.vexnum;i++) { for(j=0;j<G.vexnum;j++) printf("%10d ",G.matrix[i][j]); printf("\n"); } } EData* get_edges(Graph G) { EData *edges; edges=(EData*)malloc(G.edgnum*sizeof(EData)); int i,j; int index=0; for(i=0;i<G.vexnum;i++) { for(j=i+1;j<G.vexnum;j++) { if(G.matrix[i][j]!=INF) { edges[index].start=G.vexs[i]; edges[index].end=G.vexs[j]; edges[index].weight=G.matrix[i][j]; index++; } } } return edges; } void sort_edges(EData *edges,int elen) { int i,j; for(i=0;i<elen;i++) { for(j=i+1;j<elen;j++) { if(edges[i].weight>edges[j].weight) { EData tmp=edges[i]; edges[i]=edges[j]; edges[j]=tmp; } } } } int get_end(int vends[],int i) { while(vends[i]!=0) i=vends[i]; return i; } void kruskal(Graph G) { int i,m,n,p1,p2; int length; int index=0; int vends[MAX]={0}; EData rets[MAX]; EData *edges; edges=get_edges(G); sort_edges(edges,G.edgnum); for(i=0;i<G.edgnum;i++) printf("%d ",edges[i].weight); printf("\n"); for(i=0;i<G.edgnum;i++) { p1=get_position(G,edges[i].start); p2=get_position(G,edges[i].end); m=get_end(vends,p1); n=get_end(vends,p2); printf("m= %d,n= %d",m,n); if(m!=n) { vends[m]=n; rets[index++]=edges[i]; } } free(edges); length=0; for(i=0;i<index;i++) length+=rets[i].weight; printf("Kruskal = %d\n",length); for(i=0;i<index;i++) printf("( %c , %c ) ",rets[i].start,rets[i].end); printf("\n"); } int main() { Graph *pG; pG=create_graph(); print_graph(*pG); kruskal(*pG); }
運行結果:




