大話數據結構學習筆記 - 圖的最小生成樹之Kruskal算法

大話數據結構學習筆記 - 圖的最小生成樹之Kruskal算法

Kruskal算法

克魯斯卡爾(Kruskal)算法,是用來求加權連通圖的最小生成樹的算法

大話數據結構定義

假設 N=(V,{E}) 是連通網,則令最小生成樹的初始狀態爲只有n個頂點而無邊的非連通圖 T={V,{}} 。圖中每個頂點自成一個連通分量。在E中選擇代價最小的邊,若該邊依附的頂點落在T中不同的連通分量上,則將此邊加入到T中,否則社區此邊而選擇下一條代價最小的邊,以此類推,直至T中所有頂點都在同一連通分量上爲止。

基本思想

按照權值從小到大的順序選擇n - 1條邊,並保證這n - 1條邊不構成迴路

具體做法

首先構造一個只含n個頂點的森林,然後依權值從小到大從連通網中選擇邊加入到森林中,並使森林不產生迴路,直至森林變成過一棵樹爲止

Kruskal算法圖解

Graph_prim_G4

以上圖G4爲例,使用克魯斯卡爾算法進行演示實現最小生成樹,用parent表示

Graph_prim_G4_kruskal_details

第零步: 將鄰接矩陣轉換爲邊表數組,並且按權值大小排序

第一步: 將邊<E,F> 加入最小生成樹中

​ 邊<E,F> 的權值最小,故將其加入最小生成樹

第二步: 將邊<C,D> 加入最小生成樹中

​ 上一步操作後, 邊<C,D> 的權值最小,故將其加入最小生成樹

第三步: 將邊<D,E> 加入最小生成樹中

​ 上一步操作後, 邊<D,E> 的權值最小,故將其加入最小生成樹

第四步: 將邊<B,F> 加入最小生成樹中

​ 上一步操作後,邊<C,E> 的權值最小, 但邊<C,E> 會和最小生成樹中的已有邊構成迴路,故跳過。同理,跳過邊<C,F>

第五步:將邊<E,G> 加入到最小生成樹中

​ 上一步操作後,邊<E,G> 的權值最小,故將其加入到最小生成樹中

第六步: 將邊<A,B> 加入到最小生成樹中

​ 上一步操作後,邊<F,G> 權值最小, 但會和已有邊構成迴路,跳過。同理跳過邊<B,C> 。將邊<A,B> 加入

此時,最小生成樹構造完成,含有的依次爲<E,F><C,D><D,E><B,F><E,G><A,B>

Kruskal算法要點

  1. 對圖的所有邊按照權值大小排序

    此問題可通過代碼實例理解

  2. 將邊添加到最小生成樹中,如何判斷是否形成迴路

    通過記錄每個頂點在最小生成樹中的終點。終點即在最小生成樹中與它連通的最大頂點。每次添加一條邊到最小生成樹中時,判斷該邊的兩個頂點的終點是否重合,重合則構成迴路。

    Graph_prim_G4_kruskal_is_or_not_loop

    在將<E,F><C,D><D,E> 加入到最小生成樹中後,這幾條邊的頂點就都有了終點

    • C的終點是F
    • D的終點是F
    • E的終點是F
    • F的終點是F

    關於終點,就是將所有頂點按照從小到大的順序排列好之後;某個頂點的終點就是”與它連通的最大頂點”。 雖然邊<C,E> 權值最小,但終點都是F, 故會形成迴路

Kruskal算法代碼

Edge邊集數組結構

typedef struct
{
    int begin;
    int end;
    int weight;
}Edge;

算法

/* 生成最小生成樹 */
void MiniSpanTree_Kruskal(MGraph G)
{
    int i, j, n, m;
    int k = 0;
    int parent[MAXVEX];  /* 定義一數組用來判斷邊與邊是否形成環路 */
    Edge edges[MAXEDGE];  /* 定義邊集數組,edge的結構爲begin,end,weight,均爲整型 */

    /* 用來構建邊集數組並排序********************* */
    for(i = 0; i < G.numVertexes - 1; i++)
    {
        for(j = i + 1; j < G.numVertexes; j++)
        {
            if(G.arc[i][j] < INF)
            {
                edges[k].begin = i;
                edges[k].end = j;
                edges[k].weight = G.arc[i][j];
                k++;
            }
        }
    }
    sort(edges, &G);
    /* ******************************************* */

    printf("打印最小生成樹:\n");

    for(i = 0; i < G.numVertexes; i++)
        parent[i] = 0;  /* 初始化數組值爲0 */

    for(i = 0; i < G.numEdges; i++)  /* 循環每一條邊 */
    {
        n = Find(parent, edges[i].begin);
        m = Find(parent, edges[i].end);
        if(n != m)  /* 假如n與m不等,說明此邊沒有與現有的生成樹形成環路 */
        {
            parent[n] = m;  /* 將此邊的結尾頂點放入下標爲起點的parent中。 表示此頂點已經在生成樹集合中*/
            printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
        }
    }

}

算法源碼

鄰接矩陣源碼

參考資料

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