標籤:kruskal 最小產生樹
[本文是自己學習所做筆記,歡迎轉載,但請註明出處:http://blog.csdn.net/jesson20121020]
演算法描述:
Kruskal演算法是按權值遞增的次序來構造最小產生樹的方法。
假設G(V,E)最一個具有n個頂點的連通網,頂點集V={v1,v2,....,vn}。設所求的最小產生樹為T={U,TE},其中U是T的頂點集,TE是T的邊集,U和TE的初始值為空白集。Kruskal演算法的基本思想如下:將最小產生樹初始化為T=(V,TE),僅包含 G的全部頂點,不包含G的任一條邊,此時,T由n 個連通分量組成,每個分量只有一個頂點;將圖中G中的邊按權值從小到大排序,選擇代價最小了一條邊,若該邊所依附的頂點分屬於T中的不同的連通分量,則將此邊加入到TE中,否則,捨棄此邊而選擇下一條代價最小的邊。依次類推,直至TE中包含n-1條邊(即T中所有的頂點都在同一連通分量上)為止。
演算法實現:
設定一個edge數組儲存連通網中所有的邊,為了便於選擇當前權值最小的邊,需要將edge中的邊按權值從小到大進行排列。
而在連通分量的合并上,可以採用集合的合并方法,對於有n個頂點的連通網,設定一個數組father[0...n-1],其初始值為-1,表示n個頂點在不同的連通分量上。然後,依次掃描edge數組中的每一條邊,並尋找相關聯的兩個頂點所屬的連通分量,假設vf1和vf2為兩個頂點的所在樹的根節點的序號,若vf1不等於vf2,則表明這條邊的兩個頂點不屬於同一個連通分量,則將這條邊作為最小產生樹的邊並輸出,然後合并它們所屬的兩個連通分量。
演算法代碼:
int findFather(int father[],int v){int t = v;while(father[t] != -1)t = father[t];return t;}/* * *Kruskal演算法求最小產生樹 * */ void Kruskal_MG(MGraph MG,Edge edge[]){int father[MAX_VEX_NUM];int i,count,vf1,vf2;// 初始化father數組for(i = 0;i < MAX_VEX_NUM;i++){father[i] = -1;}i = 0;count = 0; // 統計加入最小生樹中的邊數// 遍曆任意兩個結點之間的邊while(i < MG.arcnum && count < MG.arcnum){vf1 = findFather(father,edge[i].start);vf2 = findFather(father,edge[i].end);// 如果這兩個節點不屬於同一個連通分量,則加入同一個連通分量if (vf1 != vf2){father[vf2] = vf1;count++;printf("%c,%c,%d\n",MG.vexs[edge[i].start],MG.vexs[edge[i].end],edge[i].cost);}i++;}}
其中,函數findFather的作用就是找指定節點所屬的連通分量,在這裡也就是找其所在樹的根節點在數組中的序號。
演算法說明:
對於帶權圖G中e條邊的權值的排序方法可以有多種,這裡採用的是最簡單的冒泡排序法,時間複雜度為O(n^2)。而判斷新選擇的邊的兩個頂點是否在同一個連通分量中,這個問題等價於一個在最多有n 個頂點的產生樹中遍曆尋找新選擇的邊的兩個節點是否存在的問題,所以此演算法的複雜度最壞情況下是O(n^2)。
注意:一般來講,Prim演算法的時間複雜度為O(n^2),因此適合於稠密圖,而Kruskal演算法需要對e條邊進行排序,最快的情況下複雜度為O(elog2e),因此對於稀疏圖,採用Kruskal演算法比較合適。
完整代碼:
/* * ===================================================================================== * * Filename: Kruskal.c * * Description: 最小產生樹之Kruskal演算法 * * Version: 1.0 * Created: 2015年05月06日 21時25分12秒 * Revision: none * Compiler: gcc * * Author: jesson20121020 (), [email protected] * Organization: * * ===================================================================================== */#include <stdio.h>#include <stdlib.h>#define MAX_VEX_NUM 50#define MAX_ARC_NUM 100#define UN_REACH 1000typedef char VertexType;typedef enum {DG, UDG} GraphType;typedef struct {VertexType vexs[MAX_VEX_NUM];int arcs[MAX_VEX_NUM][MAX_VEX_NUM];int vexnum, arcnum;GraphType type;} MGraph;/** * 根據名稱得到指定頂點在頂點集合中的下標 * vex 頂點 * return 如果找到,則返回下標,否則,返回0 */int getIndexOfVexs(char vex, MGraph *MG) {int i;for (i = 1; i <= MG->vexnum; i++) {if (MG->vexs[i] == vex) {return i;}}return 0;}/** * 建立鄰接矩陣 */void create_MG(MGraph *MG) {int i, j, k,weight;int v1, v2, type;char c1, c2;printf("Please input graph type DG(0) or UDG(1) :");scanf("%d", &type);if (type == 0)MG->type = DG;else if (type == 1)MG->type = UDG;else {printf("Please input correct graph type DG(0) or UDG(1)!");return;}printf("Please input vexmun : ");scanf("%d", &MG->vexnum);printf("Please input arcnum : ");scanf("%d", &MG->arcnum);getchar();for (i = 1; i <= MG->vexnum; i++) {printf("Please input %dth vex(char):", i);scanf("%c", &MG->vexs[i]);getchar();}//初始化鄰接矩陣for (i = 1; i <= MG->vexnum; i++) {for (j = 1; j <= MG->vexnum; j++) {if(i == j)MG->arcs[i][j] = 0;elseMG->arcs[i][j] = UN_REACH;}}//輸入邊的資訊,建立鄰接矩陣for (k = 1; k <= MG->arcnum; k++) {printf("Please input %dth arc v1(char) v2(char) weight(int): ", k);scanf("%c %c %d", &c1, &c2,&weight);v1 = getIndexOfVexs(c1, MG);v2 = getIndexOfVexs(c2, MG);if (MG->type == 1)MG->arcs[v1][v2] = MG->arcs[v2][v1] = weight;elseMG->arcs[v1][v2] = weight;getchar();}}/** * 列印鄰接矩陣和頂點資訊 */void print_MG(MGraph MG) {int i, j;if(MG.type == DG){printf("Graph type: Direct graph\n");}else{printf("Graph type: Undirect graph\n");}printf("Graph vertex number: %d\n",MG.vexnum);printf("Graph arc number: %d\n",MG.arcnum);printf("Vertex set:\n ");for (i = 1; i <= MG.vexnum; i++)printf("%c\t", MG.vexs[i]);printf("\nAdjacency Matrix:\n");for (i = 1; i <= MG.vexnum; i++) {j = 1;for (; j < MG.vexnum; j++) {printf("%d\t", MG.arcs[i][j]);}printf("%d\n", MG.arcs[i][j]);}}// 定義邊結構體typedef struct{int start;int end;int cost;}Edge;/* * * 由鄰接矩陣得到邊的資訊 * * */void init_edge(MGraph MG,Edge edge[]){int i,j;int count = 0;if(MG.type == 0){for(i = 1; i <= MG.vexnum;i++){for (j = 1;j <= MG.vexnum;j++){if(MG.arcs[i][j] != 0 && MG.arcs[i][j] != UN_REACH){edge[count].start = i;edge[count].end = j;edge[count].cost = MG.arcs[i][j];count++;}}}}else{for(i = 1; i <= MG.vexnum;i++){for (j = i;j <= MG.vexnum;j++){if(MG.arcs[i][j] != 0 && MG.arcs[i][j] != UN_REACH){edge[count].start = i;edge[count].end = j;edge[count].cost = MG.arcs[i][j];count++;}}}}}/* * * 將邊按權值從大到小排序 * */void sort_edge(Edge edge[],int arcnum){int i,j;Edge temp;for(i = 0; i < arcnum - 1;i++){for (j = i+1;j < arcnum;j++){if(edge[i].cost > edge[j].cost){temp = edge[i];edge[i] = edge[j];edge[j] = temp;}}}}/* * * 輸出邊的資訊 * */void print_edge(Edge edge[],int arcnum){int i = 0;while(i < arcnum){printf("%d,%d,%d\n",edge[i].start,edge[i].end,edge[i].cost);i++;}}/*** 找出指定節點的所屬的連通分量,這裡是找出其根節點在father數組中下標。**/ int findFather(int father[],int v){int t = v;while(father[t] != -1)t = father[t];return t;}/* * *Kruskal演算法求最小產生樹 * */ void Kruskal_MG(MGraph MG,Edge edge[]){int father[MAX_VEX_NUM];int i,count,vf1,vf2;// 初始化father數組for(i = 0;i < MAX_VEX_NUM;i++){father[i] = -1;}i = 0;count = 0; // 統計加入最小生樹中的邊數// 遍曆任意兩個結點之間的邊while(i < MG.arcnum && count < MG.arcnum){vf1 = findFather(father,edge[i].start);vf2 = findFather(father,edge[i].end);// 如果這兩個節點不屬於同一個連通分量,則加入同一個連通分量if (vf1 != vf2){father[vf2] = vf1;count++;printf("%c,%c,%d\n",MG.vexs[edge[i].start],MG.vexs[edge[i].end],edge[i].cost);}i++;}}/** * 主函數 */int main(void) {MGraph MG;Edge edge[MAX_ARC_NUM];create_MG(&MG);print_MG(MG);init_edge(MG,edge);sort_edge(edge,MG.arcnum);printf("the result of Kruskal:\n");Kruskal_MG(MG,edge);return EXIT_SUCCESS;}
運行示範:
[email protected]:~/develop/worksapce/c_learning/最小產生樹$ gcc -o Kruskal Kruskal.c[email protected]:~/develop/worksapce/c_learning/最小產生樹$ ./Kruskal Please input graph type DG(0) or UDG(1) :0Please input vexmun : 4Please input arcnum : 5Please input 1th vex(char):a Please input 2th vex(char):bPlease input 3th vex(char):cPlease input 4th vex(char):dPlease input 1th arc v1(char) v2(char) weight(int): a b 1Please input 2th arc v1(char) v2(char) weight(int): a c 3Please input 3th arc v1(char) v2(char) weight(int): a d 4Please input 4th arc v1(char) v2(char) weight(int): b c 2 Please input 5th arc v1(char) v2(char) weight(int): c d 3Graph type: Direct graphGraph vertex number: 4Graph arc number: 5Vertex set: abcdAdjacency Matrix:0134100002100010001000031000100010000the result of Kruskal:a,b,1b,c,2c,d,3
資料結構(C實現)------- 最小產生樹之Kruskal演算法