Kruskal算法(二)之 C++詳解


本章是克魯斯卡爾算法的C++實現。

目錄
1. 最小生成樹
2. 克魯斯卡爾算法介紹
3. 克魯斯卡爾算法圖解
4. 克魯斯卡爾算法分析
5. 克魯斯卡爾算法的代碼說明
6. 克魯斯卡爾算法的源碼

轉載請注明出處:http://www.cnblogs.com/skywang12345/

更多內容:數據結構與算法系列 目錄

最小生成樹

在含有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中。
第4步:將邊<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. 基本定義

// 邊的結構體
class EData
{
    public:
        char start; // 邊的起點
        char end;   // 邊的終點
        int weight; // 邊的權重

    public:
        EData(){}
        EData(char s, char e, int w):start(s),end(e),weight(w){}
};

EData是鄰接矩陣邊對應的結構體。

class MatrixUDG {
    #define MAX    100
    #define INF    (~(0x1<<31))        // 無窮大(即0X7FFFFFFF)
    private:
        char mVexs[MAX];    // 頂點集合
        int mVexNum;             // 頂點數
        int mEdgNum;             // 邊數
        int mMatrix[MAX][MAX];   // 鄰接矩陣

    public:
        // 創建圖(自己輸入數據)
        MatrixUDG();
        // 創建圖(用已提供的矩陣)
        //MatrixUDG(char vexs[], int vlen, char edges[][2], int elen);
        MatrixUDG(char vexs[], int vlen, int matrix[][9]);
        ~MatrixUDG();

        // 深度優先搜索遍歷圖
        void DFS();
        // 廣度優先搜索(類似於樹的層次遍歷)
        void BFS();
        // prim最小生成樹(從start開始生成最小生成樹)
        void prim(int start);
        // 克魯斯卡爾(Kruskal)最小生成樹
        void kruskal();
        // 打印矩陣隊列圖
        void print();

    private:
        // 讀取一個輸入字符
        char readChar();
        // 返回ch在mMatrix矩陣中的位置
        int getPosition(char ch);
        // 返回頂點v的第一個鄰接頂點的索引,失敗則返回-1
        int firstVertex(int v);
        // 返回頂點v相對於w的下一個鄰接頂點的索引,失敗則返回-1
        int nextVertex(int v, int w);
        // 深度優先搜索遍歷圖的遞歸實現
        void DFS(int i, int *visited);
        // 獲取圖中的邊
        EData* getEdges();
        // 對邊按照權值大小進行排序(由小到大)
        void sortEdges(EData* edges, int elen);
        // 獲取i的終點
        int getEnd(int vends[], int i);
};

MatrixUDG是鄰接矩陣對應的結構體。
mVexs用於保存頂點,mVexNum是頂點數,mEdgNum是邊數;mMatrix則是用於保存矩陣信息的二維數組。例如,mMatrix[i][j]=1,則表示"頂點i(即mVexs[i])"和"頂點j(即mVexs[j])"是鄰接點;mMatrix[i][j]=0,則表示它們不是鄰接點。

2. 克魯斯卡爾算法

/*
 * 克魯斯卡爾(Kruskal)最小生成樹
 */
void MatrixUDG::kruskal()
{
    int i,m,n,p1,p2;
    int length;
    int index = 0;          // rets數組的索引
    int vends[MAX]={0};     // 用於保存"已有最小生成樹"中每個頂點在該最小樹中的終點。
    EData rets[MAX];        // 結果數組,保存kruskal最小生成樹的邊
    EData *edges;           // 圖對應的所有邊

    // 獲取"圖中所有的邊"
    edges = getEdges();
    // 將邊按照"權"的大小進行排序(從小到大)
    sortEdges(edges, mEdgNum);

    for (i=0; i<mEdgNum; i++)
    {
        p1 = getPosition(edges[i].start);      // 獲取第i條邊的"起點"的序號
        p2 = getPosition(edges[i].end);        // 獲取第i條邊的"終點"的序號

        m = getEnd(vends, p1);                 // 獲取p1在"已有的最小生成樹"中的終點
        n = getEnd(vends, p2);                 // 獲取p2在"已有的最小生成樹"中的終點
        // 如果m!=n,意味着"邊i"與"已經添加到最小生成樹中的頂點"沒有形成環路
        if (m != n)
        {
            vends[m] = n;                       // 設置m在"已有的最小生成樹"中的終點為n
            rets[index++] = edges[i];           // 保存結果
        }
    }
    delete[] edges;

    // 統計並打印"kruskal最小生成樹"的信息
    length = 0;
    for (i = 0; i < index; i++)
        length += rets[i].weight;
    cout << "Kruskal=" << length << ": ";
    for (i = 0; i < index; i++)
        cout << "(" << rets[i].start << "," << rets[i].end << ") ";
    cout << endl;
}

克魯斯卡爾算法的源碼

這里分別給出"鄰接矩陣圖"和"鄰接表圖"的克魯斯卡爾算法源碼。

1. 鄰接矩陣源碼(MatrixUDG.cpp)

2. 鄰接表源碼(ListUDG.cpp)


免責聲明!

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



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