7、最小生成樹,克魯斯卡爾(Kruskal)算法

  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)以上我們只是作拋磚引玉。比如節點的數量,邊的數量,都可以根據實際情況來增減。

最小生成樹在實際生活中主要用來解決生活中的優化問題。比如在幾個大城市間修建路,如何修才能省時省力等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章