1)算法的基本思想:
前面我們學習過Prim算法,他是一種以某個節點出發,按權值遞增的次序選擇合適的邊來構造最小生成樹的方法,他的時間複雜度爲O(n2),與頂點有關,而與邊無邊,所以適合求邊稠密的圖的生成樹。
算法構造一顆最小生成樹的過程如下(母圖基於Prim算法部分的無向圖):
2)算法的描述:
在圖中任取一個頂點K作爲開始點,令U={k},W=V-U,其中V爲圖中所有頂點集,然後找一個頂點在U中,另一個頂點在W中的邊中最短的一條,找到後,將該邊作爲最小生成樹的樹邊保存起來,並將該邊頂點全部加入U集合中,並從W中刪去這些頂點,然後重新調整U中頂點到W中頂點的距離, 使之保持最小,再重複此過程,直到W爲空集止。
假設G=(V,E)是一個具有n個頂點的帶權無向連通圖,T= (U,TE)是G的最小生成樹,其中U是T的頂點集,TE是T的邊集,則構造最小生成樹的過程如下:
(1) 置U的初值等於V,TE的初值爲空集;
(2) 按權值從小到大的順序依次選取圖G中的邊,若選取的邊未使生成樹T形成迴路,則加入TE;若選取的邊使生成樹T形成迴路,則將其捨棄。循環執行(2),直到TE中包含(n-1)條邊爲止。
3)C語言描述:
爲實現克魯斯卡爾算法需要設置一維輔助數組E,按權值從小到大的順序存放圖的邊,數組的下標取值從0到e-1(e爲圖G邊的數目)。
假設數組E存放圖G中的所有邊,且邊已按權值從小到大的順序排列。n爲圖G的頂點個數,e爲圖G的邊數。克魯斯卡爾算法如下:
#define MAXE <最大邊數>
#define MAXV <最大頂點數>
typedef struct
{
int vex1; //邊的起始頂點
int vex2; //邊的終止頂點
int weight; //邊的權值
}Edge;
Voidkruskal(Edge E[],int n,int e)
{ inti,j,m1,m2,sn1,sn2,k;
int vset[MAXV];
for(i=0;i<n;i++) //初始化輔助數組
vset[i]=i;
k=1; //表示當前構造最小生成樹的第k條邊,初值爲1
j=0; //E中邊的下標,初值爲0
while(k<e) //生成的邊數小於e時繼續循環
{ ml=E[j].vex1;m2=E[j].vex2;//取一條邊的兩個鄰接點
sn1=vset[m1];sn2=vset[m2];
//分別得到兩個頂點所屬的集合編號
if(sn1!=sn2)
//兩頂點分屬於不同的集合,該邊是最小生成樹的一條邊
{ printf(“(m1,m2):%d\n”,E[j].weight);
k++; //生成邊數增l
for(i=0;i<n;i++) //兩個集合統一編號
if (vset[i]=sn2) //集合編號爲sn2的改爲sn1
vset[i]=sn1;
}
j++; //掃描下一條邊
}
}
4)C語言完整實現
#defineMAXE 11
#defineMAXV 10
#include"stdio.h"
typedefstruct
{
intvex1; //邊的起始頂點
intvex2; //邊的終止頂點
intweight; //邊的權值
}Edge;
void kruskal(Edge E[],int n,int e)
{ int i,j,m1,m2,sn1,sn2,k;
intvset[MAXV];
for(i=1;i<=n;i++) //初始化輔助數組
vset[i]=i;
k=1; //表示當前構造最小生成樹的第k條邊,初值爲1
j=0; //E中邊的下標,初值爲0
while(k < e) //生成的邊數小於e時繼續循環
{
m1=E[j].vex1;
m2=E[j].vex2;//取一條邊的兩個鄰接點
sn1=vset[m1];
sn2=vset[m2];
//分別得到兩個頂點所屬的集合編號
if(sn1!=sn2)
//兩頂點分屬於不同的集合,該邊是最小生成樹的一條邊
{
printf("(v%d,v%d):%d\n",m1,m2,E[j].weight);
k++; //生成邊數增l
if(k>=6) break;
for(i=1;i<=n;i++) //兩個集合統一編號
if(vset[i]==sn2) //集合編號爲sn2的改爲sn1
vset[i]=sn1;
}//if
j++; //掃描下一條邊
}//while
}//kruskal
intmain()
{
EdgeE[MAXE];
intnume,numn;
//printf("輸入邊數和頂數:\n");
//scanf("%d%d",&nume,&numn);
nume=10;
numn=6;
printf("請輸入各邊及對應的的權值(起始頂點 終止頂點 權值)\n");
/*
for(inti=0;i<nume;i++)
scanf("%d%d%d",E[i].vex1,E[i].vex2,E[i].weight);
*/
//在這裏對輸入的數據進行排序,達到假設的要求,即:假設數組E存放圖G中的
//所有邊,且邊已按權值從小到大的順序排列
E[9].vex1=1;
E[9].vex2=2;
E[9].weight=6;
E[0].vex1=1;
E[0].vex2=3;
E[0].weight=1;
E[4].vex1=1;
E[4].vex2=4;
E[4].weight=5;
E[6].vex1=2;
E[6].vex2=3;
E[6].weight=5;
E[2].vex1=2;
E[2].vex2=5;
E[2].weight=3;
E[8].vex1=1;
E[8].vex2=2;
E[8].weight=6;
E[5].vex1=3;
E[5].vex2=4;
E[5].weight=5;
E[7].vex1=3;
E[7].vex2=5;
E[7].weight=6;
E[3].vex1=3;
E[3].vex2=6;
E[3].weight=4;
E[1].vex1=4;
E[1].vex2=6;
E[1].weight=2;
kruskal(E,numn,nume);
}
5)以上我們只是作拋磚引玉。比如節點的數量,邊的數量,都可以根據實際情況來增減。
最小生成樹在實際生活中主要用來解決生活中的優化問題。比如在幾個大城市間修建路,如何修才能省時省力等。