克魯斯卡爾算法


環境: Codeblocks 13.12 + GCC 4.7.1


 

基本思想:(1)構造一個只含n個頂點,邊集為空的子圖。若將圖中各個頂點看成一棵樹的根節點,則它是一個含有n棵樹的森林。(2)從網的邊集 E 中選取一條權值最小的邊,若該條邊的兩個頂點分屬不同的樹,則將其加入子圖。也就是說,將這兩個頂點分別所在的兩棵樹合成一棵樹;反之,若該條邊的兩個頂點已落在同一棵樹上,則不可取,而應該取下一條權值最小的邊再試之(3)依次類推,直至森林中只有一棵樹,也即子圖中含有 n-1條邊為止。

大白話:(1)將圖中的所有邊都去掉。(2)將邊按權值從小到大的順序添加到圖中,保證添加的過程中不會形成環(3)重復上一步直到連接所有頂點,此時就生成了最小生成樹。這是一種貪心策略。


 

難點:判斷某條邊<u, v>的加入是否會在已經選定的邊集集合中形成環。

解決辦法:使用並查集,分別找出兩個頂點u, v所在樹的根節點。若根節點相同,說明u, v在同一棵樹中,則u, v連接起來會形成環;若根節點不同,則u, v不在一棵樹中,連接起來不會形成環,而是將兩棵樹合並。


 

 

圖解過程:原圖如下                                               邊集數組按權值順序排列

           

                

邊<1, 2>和<4, 5>在添加到圖中的時候形成了環,所以不能將v1和v2,v4和v5連起來。


 

判斷是否成環

int Find(int *parent, int f) {
    while ( parent[f] > 0) {
        f = parent[f];
    }
    return f;
}
    for (i = 0; i < G.numEdges; i++) {
        n = Find(parent, edges[i].begin);//尋找邊edge[i]的“首節點”所在樹的樹根
        m = Find(parent, edges[i].end);//尋找邊edge[i]的“尾節點”所在樹的樹根

        //假如n與m不等,說明兩個頂點不在一棵樹內,因此這條邊的加入不會使已經選擇的邊集產生回路
        if (n != m) {
            parent[n] = m;
            printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
        }
    }

代碼:

//克魯斯卡爾算法
//在連通網中求出最小生成樹

#include <stdio.h>
#include <stdlib.h>

#define MAXEDGE 20
#define MAXVEX  20
#define INFINITY 65535

typedef struct
{
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;//頂點數,邊數
}MGraph;

typedef struct
{
    int begin;
    int end;
    int weight;
}Edge;   //對邊集數組Edge結構的定義

//創建圖的鄰接矩陣
void CreateMGraph(MGraph *G) {
    int i, j;

    G->numEdges=11;
    G->numVertexes=7;

    for (i = 0; i < G->numVertexes; i++) {
        for ( j = 0; j < G->numVertexes; j++) {
            if (i==j)
                G->arc[i][j]=0;
            else
                G->arc[i][j] = G->arc[j][i] = INFINITY;
        }
    }
    G->arc[0][1]=7;
    G->arc[0][3]=5;
    G->arc[1][2]=8;
    G->arc[1][3]=9;
    G->arc[1][4]=7;
    G->arc[2][4]=5;
    G->arc[3][4]=15;
    G->arc[3][5]=6;
    G->arc[4][5]=8;
    G->arc[4][6]=9;
    G->arc[5][6]=11;

    for(i = 0; i < G->numVertexes; i++) {
        for(j = i; j < G->numVertexes; j++) {
            G->arc[j][i] =G->arc[i][j];
        }
    }

}

//快速排序的條件
int cmp(const void* a, const void* b) {
    return (*(Edge*)a).weight - (*(Edge*)b).weight;
}

//找到根節點
int Find(int *parent, int f) {
    while ( parent[f] > 0) {
        f = parent[f];
    }
    return f;
}

// 生成最小生成樹
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] < INFINITY) {
                edges[k].begin = i; //編號較小的結點為首
                edges[k].end = j;   //編號較大的結點為尾
                edges[k].weight = G.arc[i][j];
                k++;
            }
        }
    }

    //為邊集數組Edge排序
    qsort(edges, G.numEdges, sizeof(Edge), cmp);

    for (i = 0; i < G.numVertexes; i++)
        parent[i] = 0;

    printf("打印最小生成樹:\n");
    for (i = 0; i < G.numEdges; i++) {
        n = Find(parent, edges[i].begin);//尋找邊edge[i]的“首節點”所在樹的樹根
        m = Find(parent, edges[i].end);//尋找邊edge[i]的“尾節點”所在樹的樹根

        //假如n與m不等,說明兩個頂點不在一棵樹內,因此這條邊的加入不會使已經選擇的邊集產生回路
        if (n != m) {
            parent[n] = m;
            printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
        }
    }
}

int main(void)
{
    MGraph G;
    CreateMGraph(&G);
    MiniSpanTree_Kruskal(G);

    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM