Kruskal(克魯斯卡爾) 最小產生樹 演算法詳解+模板

來源:互聯網
上載者:User
最小產生樹

在含有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中。
第四步:將邊(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是鄰接矩陣邊對應的結構體。
轉自 http://www.cnblogs.com/skywang12345/
2. 克魯斯卡爾演算法
模板1

/* * 克魯斯卡爾(Kruskal)最小產生樹 */void kruskal(Graph G){    int i,m,n,p1,p2;    int length;    int index = 0;          // rets數組的索引    int vends[MAX]={0};     // 用於儲存"已有最小產生樹"中每個頂點在該最小樹中的終點。    EData rets[MAX];        // 結果數組,儲存kruskal最小產生樹的邊    EData *edges;           // 圖對應的所有邊    // 擷取"圖中所有的邊"    edges = get_edges(G);    // 將邊按照"權"的大小進行排序(從小到大)    sorted_edges(edges, G.edgnum);    for (i=0; i<G.edgnum; i++)    {        p1 = get_position(G, edges[i].start);   // 擷取第i條邊的"起點"的序號        p2 = get_position(G, edges[i].end);     // 擷取第i條邊的"終點"的序號        m = get_end(vends, p1);                 // 擷取p1在"已有的最小產生樹"中的終點        n = get_end(vends, p2);                 // 擷取p2在"已有的最小產生樹"中的終點        // 如果m!=n,意味著"邊i"與"已經添加到最小產生樹中的頂點"沒有形成環路        if (m != n)        {            vends[m] = n;                       // 設定m在"已有的最小產生樹"中的終點為n            rets[index++] = edges[i];           // 儲存結果        }    }    free(edges);    // 統計並列印"kruskal最小產生樹"的資訊    length = 0;    for (i = 0; i < index; i++)        length += rets[i].weight;    printf("Kruskal=%d: ", length);    for (i = 0; i < index; i++)        printf("(%c,%c) ", rets[i].start, rets[i].end);    printf("\n");}

模板2

#include<stdio.h>#include<string.h>#define inf 0x3f3f3f3fint map[110][110],dis[110],visit[110];/*關於三個數組:map數組存的為點邊的資訊,比如map[1][2]=3,表示1號點和2號點的距離為3dis數組存的為起始點與每個點的最短距離,比如dis[3]=5,表示起始點與3號點最短距離為5visit數組存的為0或者1,1表示已經走過這個點。*/int n,m;int dijstra(){    int i,j,pos=1,min,sum=0;    memset(visit,0,sizeof(visit));//初始化為.,表示開始都沒走過    for(i=1; i<=n; ++i)    {        dis[i]=map[1][i];    }    visit[1]=1;    dis[1]=0;    for(i=1; i<n; i++)    {        min=inf;        for(j=1; j<=n; ++j)        {            if(visit[j]==0&&min>dis[j])            {                min=dis[j];                pos=j;            }        }        visit[pos]=1;//表示這個點已經走過        for(j=1; j<=n; ++j)        {            if(visit[j]==0&&dis[j]>dis[pos]+map[pos][j])//更新dis的值                dis[j]=dis[pos]+map[pos][j];        }    }    return dis[n];}int main(){    int i,j;    while(~scanf("%d%d",&n,&m),n||m)//n表示n個點,m表示m條邊    {        for(i=1; i<=n; ++i)        {            for(j=1; j<=n; ++j)            {                map[i][j]=inf;//開始時將每條邊賦為最大值            }        }        int a,b,c;        for(i=1; i<=m; ++i)        {            scanf("%d%d%d",&a,&b,&c);            if(c<map[a][b])//防止有重邊                map[a][b]=map[b][a]=c;        }        int count=dijstra();        printf("%d\n",count);    }    return 0;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.