「圖論」最小生成樹-Prime算法

Prim算法

1.概覽

普里姆算法Prim算法),圖論中的一種算法,可在加權連通圖裏搜索最小生成樹。意即由此算法搜索到的邊子集所構成的樹中,不但包括了連通圖裏的所有頂點英語Vertex (graph theory),且其所有邊的權值之和亦爲最小。該算法於1930年由捷克數學家沃伊捷赫·亞爾尼克英語Vojtěch Jarník發現;並在1957年由美國計算機科學家羅伯特·普里姆英語Robert C. Prim獨立發現;1959年,艾茲格·迪科斯徹再次發現了該算法。因此,在某些場合,普里姆算法又被稱爲DJP算法、亞爾尼克算法或普里姆-亞爾尼克算法。

 

2.算法簡單描述

1).輸入:一個加權連通圖,其中頂點集合爲V,邊集合爲E;

2).初始化:Vnew = {x},其中x爲集合V中的任一節點(起始點),Enew = {},爲空;

3).重複下列操作,直到Vnew = V:

a.在集合E中選取權值最小的邊<u, v>,其中u爲集合Vnew中的元素,而v不在Vnew集合當中,並且v∈V(如果存在有多條滿足前述條件即具有相同權值的邊,則可任意選取其中之一);

b.將v加入集合Vnew中,將<u, v>邊加入集合Enew中;

4).輸出:使用集合Vnew和Enew來描述所得到的最小生成樹。

 

下面對算法的圖例描述

圖例 說明 不可選 可選 已選(Vnew
 

此爲原始的加權連通圖。每條邊一側的數字代表其權值。 - - -

頂點D被任意選爲起始點。頂點ABEF通過單條邊與D相連。A是距離D最近的頂點,因此將A及對應邊AD以高亮表示。 C, G A, B, E, F D
 

下一個頂點爲距離DA最近的頂點。BD爲9,距A爲7,E爲15,F爲6。因此,FDA最近,因此將頂點F與相應邊DF以高亮表示。 C, G B, E, F A, D
算法繼續重複上面的步驟。距離A爲7的頂點B被高亮表示。 C B, E, G A, D, F
 

在當前情況下,可以在CEG間進行選擇。CB爲8,EB爲7,GF爲11。E最近,因此將頂點E與相應邊BE高亮表示。 C, E, G A, D, F, B
 

這裏,可供選擇的頂點只有CGCE爲5,GE爲9,故選取C,並與邊EC一同高亮表示。 C, G A, D, F, B, E

頂點G是唯一剩下的頂點,它距F爲11,距E爲9,E最近,故高亮表示G及相應邊EG G A, D, F, B, E, C

現在,所有頂點均已被選取,圖中綠色部分即爲連通圖的最小生成樹。在此例中,最小生成樹的權值之和爲39。 A, D, F, B, E, C, G

 

3.簡單證明prim算法

反證法:假設prim生成的不是最小生成樹

1).設prim生成的樹爲G0

2).假設存在Gmin使得cost(Gmin)<cost(G0)   則在Gmin中存在<u,v>不屬於G0

3).將<u,v>加入G0中可得一個環,且<u,v>不是該環的最長邊(這是因爲<u,v>∈Gmin)

4).這與prim每次生成最短邊矛盾

5).故假設不成立,命題得證.


#define MAX  100000
#define VNUM  10+1                                             //這裏沒有ID爲0的點,so id號範圍1~10

int edge[VNUM][VNUM]={/*輸入的鄰接矩陣*/};
int lowcost[VNUM]={0};                                         //記錄Vnew中每個點到V中鄰接點的最短邊
int addvnew[VNUM];                                             //標記某點是否加入Vnew
int adjecent[VNUM]={0};                                        //記錄V中與Vnew最鄰近的點


void prim(int start)
{
     int sumweight=0;
     int i,j,k=0;

     for(i=1;i<VNUM;i++)                                      //頂點是從1開始
     {
        lowcost[i]=edge[start][i];
        addvnew[i]=-1;                                         //將所有點至於Vnew之外,V之內,這裏只要對應的爲-1,就表示在Vnew之外
     }

     addvnew[start]=0;                                        //將起始點start加入Vnew
     adjecent[start]=start;
                                                 
     for(i=1;i<VNUM-1;i++)                                        
     {
        int min=MAX;
        int v=-1;
        for(j=1;j<VNUM;j++)                                      
        {
            if(addvnew[j]!=-1&&lowcost[j]<min)                 //在Vnew之外尋找最短路徑
            {
                min=lowcost[j];
                v=j;
            }
        }
        if(v!=-1)
        {
            printf("%d %d %d\n",adjecent[v],v,lowcost[v]);
            addvnew[v]=0;                                      //將v加Vnew中

            sumweight+=lowcost[v];                             //計算路徑長度之和
            for(j=1;j<VNUM;j++)
            {
                if(addvnew[j]==-1&&edge[v][j]<lowcost[j])      
                {
                    lowcost[j]=edge[v][j];                     //此時v點加入Vnew 需要更新lowcost
                    adjecent[j]=v;                             
                }
            }
        }
    }
    printf("the minmum weight is %d",sumweight);
}


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