數據結構編程筆記二十:第七章 圖 最小生成樹算法的實現

上次我們介紹了圖的鄰接表存儲結構基本操作的實現,這次介紹基於鄰接矩陣的兩種最小生成樹算法的實現。

還是老規矩:

程序在碼雲上可以下載。
地址:https://git.oschina.net/601345138/DataStructureCLanguage.git

最小生成樹有兩個經典的算法:
1.普里姆算法:主要是針對頂點操作,選最小鄰接邊組成生成樹。
2.克魯斯卡爾算法:主要針對邊操作,所有邊裏選最小權值的邊組成生成樹。

本次最小生成樹共用到以下源文件,有一些已經在之前的文章介紹過。還是和以前一樣,所有源文件需要放在同一目錄下編譯。
my_constants.h 各種狀態碼定義
MGraph.h 圖的鄰接矩陣存儲結構表示定義
MGraph.cpp 基於鄰接矩陣的基本操作實現
MiniSpanTree.cpp 普利姆算法和克魯斯卡爾算法的實現(含有動態演示代碼)
最小生成樹測試.cpp 主函數,調用算法完成最小生成樹的動態演示

鄰接矩陣在《數據結構編程筆記十八:第七章 圖 圖的鄰接矩陣存儲表示及各基本操作的實現》一文有所介紹,my_constants.h、MGraph.h 和MGraph.cpp三個源文件與此文中的同名源文件內容完全一致,沒有修改。這裏不再重複貼了(否則文章會很長,不能突出重點),但在碼雲上你可以下載到全部源文件,我會把它放在一個目錄下

本次只貼最小生成樹的核心代碼和主函數:

源文件:MiniSpanTree.cpp

//-------------- 最小生成樹 ----------------------------

//-------------------輔助數組的定義------------------------------- 
struct Record{
    VertexType adjvex;   //頂點 
    VRType     lowcost;  //最低代價 
}closedge[MAX_VERTEX_NUM];

/*
    函數:printColsedge
    參數:Record closedge[] 計算最小生成樹的輔助數組closedge 
          int n 頂點數 
    返回值:無 
    作用:打印closedge數組 
*/
void printColsedge(Record closedge[], MGraph G) {

    //打印i一行 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

    printf("|     i    |"); 

    for(int i = 0; i < G.vexnum; i++) {

        printf("  %2d |", i);
    }//for 

    printf("\n"); 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

    //頂點值 
    printf("|   頂點   |"); 

    for(int i = 0; i < G.vexnum; i++) {

        printf("  %2d |", G.vexs[i]);
    }//for 

    printf("\n"); 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

    //打印adjvex一行 

    printf("|  adjvex  |"); 

    for(int i = 0; i < G.vexnum; i++) {

        printf(" %3d |", closedge[i].adjvex);
    }//for 

    printf("\n"); 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

    //打印lowcost一行 

    printf("|  lowcost |"); 

    for(int i = 0; i < G.vexnum; i++) {

        if(closedge[i].lowcost != INFINITY) {
            printf(" %3d |", closedge[i].lowcost);
        }//if
        else {
            printf("  ∞ |"); 
        }//else 

    }//for 

    printf("\n"); 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

}//printColsedge

/*
    函數:minimum
    參數:Record closedge[] 計算最小生成樹的輔助數組closedge 
    返回值:選出頂點的序號 
    作用:從closedge數組中選出符合條件的最小生成樹的下一頂點 
*/
int minimum(Record closedge[]){

    //min是從closedge數組中選出的最小代價邊的另一個頂點在圖中的位置
    //reserve是輔助參與比較的最小代價,初始值爲65535,
    //每一次發現代價比它更小的邊則reserve會被更新爲新的最小代價 
    int reserve = 65535, min = 0;

    for(int i = 1; i < MAX_VERTEX_NUM; i++) {

        //沒有訪問過但是存在路徑並且代價更小(比記錄最小代價的reserve還小) 
        if(closedge[i].lowcost < reserve && closedge[i].lowcost > 0){

            printf("->發現比reserve = %d更小的權值:closedge[%d].lowcost = %d !\n",
                        reserve, i, closedge[i].lowcost);

            //reserve更新爲新的最小代價 
            reserve = closedge[i].lowcost;

            //min更新爲最小代價邊的另一頂點在圖中的位置 
            min = i;
        }//if
    }//for

    return min;
}//minimum

/* (普里姆算法求最小生成樹) 
    函數:MiniSpanTree_PRIM
    參數:MGraph G 圖G 
    返回值:無 
    作用:用普里姆算法從第u個頂點出發構造網G的最小生成樹T,輸出T的各條邊
          記錄從定點集U到V-U的代價最小的邊的輔助數組定義
*/
void MiniSpanTree_PRIM(MGraph G, VertexType u){

    //k是出發頂點u在圖中的序號 
    int k = LocateVex(G, u);

    //輔助數組初始化
    for(int j = 0; j < G.vexnum; ++j) {  

        //j指示的頂點不是出發頂點 
        if(j != k){
            //{adjvex, lowcost}
            //設置鄰接點爲起始頂點u 
            closedge[j].adjvex = u;

            //設置closedge數組初始最小代價,其實就是
            //直接拷貝第G.arcs二維數組的第k行 
            closedge[j].lowcost = G.arcs[k][j].adj;
        }//if
    }//for

    //設置出發點u的最小代價爲0,此時U={u}  
    closedge[k].lowcost = 0;

    printf("\n->最小生成樹從頂點%d開始,所以該頂點此時已被加入最小生成樹頂點集合!\n\n", G.vexs[k]);

    //從頂點u開始生成最小生成樹 
    for(int i = 1; i < G.vexnum; ++i) {

        printf("\n->這是第%d趟循環:\n" , i);

        printf("->更新closedge數組之前,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):\n");

        //打印closedge數組 
        printColsedge(closedge, G); 

        printf("->開始求最小生成樹的下一頂點:\n"); 

        //求出最小生成樹的下一個結點:第k頂點
        //此時closedge[k].lowcost= MIN{ closedge[vi].lowcost 
        //      | closedge[vi].lowcost > 0, vi ∈V-U} 
        k = minimum(closedge);

        printf("->求得最小生成樹的下一頂點爲:%d,下一頂點序號k = %d, 最小代價爲:%d\n", G.vexs[k], k, closedge[k].lowcost);

        //輸出生成樹的邊及其權值
        printf("\n->得到最小生成樹的一條新邊: %2d <--- %2d ---> %2d \n\n", closedge[k].adjvex, 
            closedge[k].lowcost, G.vexs[k]);

        //第k頂點併入U集
        closedge[k].lowcost = 0;

        //查找k的鄰接頂點中代價更小的邊對應的鄰接頂點 
        //將新頂點併入U後重新選擇最小邊
        //選出一個頂點k之後需要更新closedge數組中最小邊信息
        for(int j = 1; j < G.vexnum; ++j) { 

            //發現代價更小的邊就更新closedge數組
            //若沒有發現則保持原值不變 
            if(G.arcs[k][j].adj < closedge[j].lowcost){

                printf("從%d的所有鄰接頂點中發現有G.arcs[%d][%d].adj = %d 比 closedge[%d].lowcost = %d 更小,更新closedge數組!\n",
                        G.vexs[k], k, j, G.arcs[k][j].adj, j, closedge[j].lowcost); 

                //更新closedge數組的鄰接點信息adjvex 
                closedge[j].adjvex = G.vexs[k];

                //更新closedge數組的最小邊信息lowcost 
                closedge[j].lowcost = G.arcs[k][j].adj; 
            }//if
        }//for 

        printf("\n->更新closedge數組之後,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):\n");

        //打印closedge數組 
        printColsedge(closedge, G); 

        system("pause");
    }//for    
}//MiniSpanTree_PRIM 


/*
    函數:printSet
    參數:int set[] 保存頂點所屬集合狀態的數組set 
          MGraph G 圖G 
    返回值:無 
    作用:打印set數組 
*/
void printSet(int set[], MGraph G) {

    //打印i一行 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

    printf("|     i    |"); 

    for(int i = 0; i < G.vexnum; i++) {

        printf("  %2d |", i);
    }//for 

    printf("\n"); 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

    //頂點值 
    printf("|   頂點   |"); 

    for(int i = 0; i < G.vexnum; i++) {

        printf("  %2d |", G.vexs[i]);
    }//for 

    printf("\n"); 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

    //打印set一行 

    printf("|    set   |"); 

    for(int i = 0; i < G.vexnum; i++) {

        printf(" %3d |", set[i]);
    }//for 

    printf("\n"); 

    printf("+----------+");

    for(int i = 0; i < G.vexnum; i++) {

        printf("-----+");
    }//for 

    printf("\n"); 

}//printSet

/* (克魯斯卡爾算法求最小生成樹) 
    函數:MiniSpanTree_kruskal
    參數:MGraph G 圖G 
    返回值:無 
    作用:克魯斯卡爾算法求最小生成樹 
*/
void MiniSpanTree_kruskal(MGraph G) {

    int set[MAX_VERTEX_NUM];
    int k = 0, a = 0, b = 0, setb, min = G.arcs[a][b].adj;

    //set[]初態,各頂點分別屬於各個集合
    for(int i = 0; i < G.vexnum; i++) {
        set[i] = i; 
    }//for

    printf("->set數組初始狀態(各頂點分別屬於各個集合):\n");
    printSet(set, G);

    printf("最小代價生成樹的各條邊及權值爲:\n");
    //最小生成樹的邊數應該有n-1條邊,其中n爲頂點數 
    while(k < G.vexnum-1) {

        printf("\n->這是第%d趟循環,尋找更小權值的邊:\n", k + 1);

        //尋找最小權值的邊
        for(int i = 0; i < G.vexnum; ++i) { 

            //無向網,只在上三角查找
            for(int j = i + 1; j < G.vexnum; ++j) {

                //發現比min更小的權值 
                if(G.arcs[i][j].adj < min) {

                    printf("->發現比min = %d 更小的權值:%d,修改min爲該值!\n", min, G.arcs[i][j].adj);

                    //最小權值
                    min = G.arcs[i][j].adj; 

                    //邊的一個頂點
                    a = i;

                    //邊的另一個頂點
                    b = j;
                }//if
            }//for
        }//for

        //邊的兩頂點不屬於同一集合
        if(set[a] != set[b]) {

            //輸出該邊
            printf("->找到一條最小生成樹的新邊:%d <-- %d --> %d\n", G.vexs[a], G.arcs[a][b].adj, G.vexs[b]);
        }//if 

        //重置min爲極大值 
        min = G.arcs[a][b].adj = INFINITY;

        //邊的兩頂點不屬於同一集合
        if(set[a] != set[b]) {

            //邊數+1
            k++;

            //保存setb的值
            setb = set[b];

            //將頂點b所在集合併入頂點a集合中
            for(int i = 0; i < G.vexnum; i++) {

                //不可以直接set[i] == set[b]作爲比較條件,因爲i可能等於b! 
                //一旦set[b]被修改,那麼set[b]後面的元素將無法正確合併  
                if(set[i] == setb) {
                    printf("->合併頂點%d到頂點%d的集合中,注意set數組的變化。\n", G.vexs[i], G.vexs[a]);
                    set[i] = set[a];
                }//if
            }//for 

            printf("->set數組修改後的值:\n");                                                
            printSet(set, G);
        }//if 
    }//while

    printf("->set數組初最終狀態(無向連通網所有頂點應該都在一個集合裏):\n");
    printSet(set, G);
}//MiniSpanTree_kruskal

源文件:最小生成樹測試.cpp

//**************************引入頭文件*****************************
#include <stdio.h>   //使用了標準庫函數 
#include <stdlib.h>  //使用了動態內存分配函數

#include "my_constants.h" //引入自定義的符號常量,主要是狀態碼 
#include "MGraph.h"       //引入圖的鄰接矩陣表示法的基本定義 
#include "MGraph.cpp"     //引入圖的主要操作
#include "MiniSpanTree.cpp" //引入最小生成樹實現 

//----------------------主函數----------------------
int main(int argc, char *argv[]){

    printf("\n-------------最小生成樹(鄰接矩陣)測試程序--------------\n\n"); 

    //圖G 
    MGraph G; 

    //臨時變量,保存輸入的頂點數 
    int n;

    //圖的創建
    printf("->測試圖的創建(最小生成樹操作要求圖的類型是無向連通網,請選擇3):\n");
    CreateGraph(G);

    //打印鄰接矩陣
    printf("\n->創建成功後圖的鄰接矩陣:\n\n"); 
    PrintAdjMatrix(G);

    //若爲無向網,求其最小生成樹
    if(G.kind == UDN){
        printf("\n->測試普利姆算法求無向網的最小生成樹\n");
        MiniSpanTree_PRIM(G, G.vexs[0]);  

        printf("\n->測試克魯斯卡爾算法求無向網的最小生成樹\n");
        MiniSpanTree_kruskal(G);  
    }//if 
    else {
        printf("\n->您生成的不是無向網,無法求最小生成樹!\n");
    }//else 

    //測試銷燬 
    printf("\n->測試銷燬圖: ");
    DestroyGraph(G); 
    printf("成功!\n");

    printf("演示結束,程序退出!\n");

    return 0;
} 

程序運行輸入的數據和輸出(輸入的無向圖來自教材P174頁,你可以對比圖7.17的數據和輸出的數據是否一致):

-------------最小生成樹(鄰接矩陣)測試程序--------------

->測試圖的創建(最小生成樹操作要求圖的類型是無向連通網,請選擇3):
請輸入您想構造的圖的類型:有向圖輸入0,有向網輸入1,無向圖輸入2,無向網輸入3):3
請依次輸入無向網G的頂點數,弧數,用逗號隔開
6,10
請依次輸入無向網G的頂點名稱,用空格隔開
1 2 3 4 5 6
請依次輸入無向網G每條弧依附的兩頂點名稱及權值,輸完一組按回車
1 2 6
1 4 5
1 3 1
3 2 5
3 4 5
3 5 6
3 6 4
2 5 3
4 6 2
5 6 6

->創建成功後圖的鄰接矩陣:

         1    2    3    4    5    6
     +------------------------------
   1 |  ∞    6    1    5   ∞   ∞
     |
   2 |   6   ∞    5   ∞    3   ∞
     |
   3 |   1    5   ∞    5    6    4
     |
   4 |   5   ∞    5   ∞   ∞    2
     |
   5 |  ∞    3    6   ∞   ∞    6
     |
   6 |  ∞   ∞    4    2    6   ∞
     |

->測試普利姆算法求無向網的最小生成樹

->最小生成樹從頂點1開始,所以該頂點此時已被加入最小生成樹頂點集合!


->這是第1趟循環:
->更新closedge數組之前,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   1 |   1 |   1 |   1 |   1 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   6 |   1 |   5 |  ∞ |  ∞ |
+----------+-----+-----+-----+-----+-----+-----+
->開始求最小生成樹的下一頂點:
->發現比reserve = 65535更小的權值:closedge[1].lowcost = 6 !
->發現比reserve = 6更小的權值:closedge[2].lowcost = 1 !
->求得最小生成樹的下一頂點爲:3,下一頂點序號k = 2, 最小代價爲:1

->得到最小生成樹的一條新邊:  1 <---  1 --->  3

從3的所有鄰接頂點中發現有G.arcs[2][1].adj = 5 比 closedge[1].lowcost = 6 更小,更新closedge數組!
從3的所有鄰接頂點中發現有G.arcs[2][4].adj = 6 比 closedge[4].lowcost = 65535 更小,更新closedge數組!
從3的所有鄰接頂點中發現有G.arcs[2][5].adj = 4 比 closedge[5].lowcost = 65535 更小,更新closedge數組!

->更新closedge數組之後,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   1 |   3 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   5 |   0 |   5 |   6 |   4 |
+----------+-----+-----+-----+-----+-----+-----+
請按任意鍵繼續. . .

->這是第2趟循環:
->更新closedge數組之前,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   1 |   3 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   5 |   0 |   5 |   6 |   4 |
+----------+-----+-----+-----+-----+-----+-----+
->開始求最小生成樹的下一頂點:
->發現比reserve = 65535更小的權值:closedge[1].lowcost = 5 !
->發現比reserve = 5更小的權值:closedge[5].lowcost = 4 !
->求得最小生成樹的下一頂點爲:6,下一頂點序號k = 5, 最小代價爲:4

->得到最小生成樹的一條新邊:  3 <---  4 --->  6

從6的所有鄰接頂點中發現有G.arcs[5][3].adj = 2 比 closedge[3].lowcost = 5 更小,更新closedge數組!

->更新closedge數組之後,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   6 |   3 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   5 |   0 |   2 |   6 |   0 |
+----------+-----+-----+-----+-----+-----+-----+
請按任意鍵繼續. . .

->這是第3趟循環:
->更新closedge數組之前,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   6 |   3 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   5 |   0 |   2 |   6 |   0 |
+----------+-----+-----+-----+-----+-----+-----+
->開始求最小生成樹的下一頂點:
->發現比reserve = 65535更小的權值:closedge[1].lowcost = 5 !
->發現比reserve = 5更小的權值:closedge[3].lowcost = 2 !
->求得最小生成樹的下一頂點爲:4,下一頂點序號k = 3, 最小代價爲:2

->得到最小生成樹的一條新邊:  6 <---  2 --->  4


->更新closedge數組之後,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   6 |   3 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   5 |   0 |   0 |   6 |   0 |
+----------+-----+-----+-----+-----+-----+-----+
請按任意鍵繼續. . .

->這是第4趟循環:
->更新closedge數組之前,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   6 |   3 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   5 |   0 |   0 |   6 |   0 |
+----------+-----+-----+-----+-----+-----+-----+
->開始求最小生成樹的下一頂點:
->發現比reserve = 65535更小的權值:closedge[1].lowcost = 5 !
->求得最小生成樹的下一頂點爲:2,下一頂點序號k = 1, 最小代價爲:5

->得到最小生成樹的一條新邊:  3 <---  5 --->  2

從2的所有鄰接頂點中發現有G.arcs[1][4].adj = 3 比 closedge[4].lowcost = 6 更小,更新closedge數組!

->更新closedge數組之後,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   6 |   2 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   0 |   0 |   0 |   3 |   0 |
+----------+-----+-----+-----+-----+-----+-----+
請按任意鍵繼續. . .

->這是第5趟循環:
->更新closedge數組之前,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   6 |   2 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   0 |   0 |   0 |   3 |   0 |
+----------+-----+-----+-----+-----+-----+-----+
->開始求最小生成樹的下一頂點:
->發現比reserve = 65535更小的權值:closedge[4].lowcost = 3 !
->求得最小生成樹的下一頂點爲:5,下一頂點序號k = 4, 最小代價爲:3

->得到最小生成樹的一條新邊:  2 <---  3 --->  5


->更新closedge數組之後,closedge數組的值(adjvex是頂點序號,不是頂點名稱,lowcost = 0表示該頂點已被納入最小生成樹的頂點集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|  adjvex  |   0 |   3 |   1 |   6 |   2 |   3 |
+----------+-----+-----+-----+-----+-----+-----+
|  lowcost |   0 |   0 |   0 |   0 |   0 |   0 |
+----------+-----+-----+-----+-----+-----+-----+
請按任意鍵繼續. . .

->測試克魯斯卡爾算法求無向網的最小生成樹
->set數組初始狀態(各頂點分別屬於各個集合):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|    set   |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
最小代價生成樹的各條邊及權值爲:

->這是第1趟循環,尋找更小權值的邊:
->發現比min = 65535 更小的權值:6,修改min爲該值!
->發現比min = 6 更小的權值:1,修改min爲該值!
->找到一條最小生成樹的新邊:1 <-- 1 --> 3
->合併頂點3到頂點1的集合中,注意set數組的變化。
->set數組修改後的值:
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|    set   |   0 |   1 |   0 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+

->這是第2趟循環,尋找更小權值的邊:
->發現比min = 65535 更小的權值:6,修改min爲該值!
->發現比min = 6 更小的權值:5,修改min爲該值!
->發現比min = 5 更小的權值:3,修改min爲該值!
->發現比min = 3 更小的權值:2,修改min爲該值!
->找到一條最小生成樹的新邊:4 <-- 2 --> 6
->合併頂點6到頂點4的集合中,注意set數組的變化。
->set數組修改後的值:
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|    set   |   0 |   1 |   0 |   3 |   4 |   3 |
+----------+-----+-----+-----+-----+-----+-----+

->這是第3趟循環,尋找更小權值的邊:
->發現比min = 65535 更小的權值:6,修改min爲該值!
->發現比min = 6 更小的權值:5,修改min爲該值!
->發現比min = 5 更小的權值:3,修改min爲該值!
->找到一條最小生成樹的新邊:2 <-- 3 --> 5
->合併頂點5到頂點2的集合中,注意set數組的變化。
->set數組修改後的值:
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|    set   |   0 |   1 |   0 |   3 |   1 |   3 |
+----------+-----+-----+-----+-----+-----+-----+

->這是第4趟循環,尋找更小權值的邊:
->發現比min = 65535 更小的權值:6,修改min爲該值!
->發現比min = 6 更小的權值:5,修改min爲該值!
->發現比min = 5 更小的權值:4,修改min爲該值!
->找到一條最小生成樹的新邊:3 <-- 4 --> 6
->合併頂點4到頂點3的集合中,注意set數組的變化。
->合併頂點6到頂點3的集合中,注意set數組的變化。
->set數組修改後的值:
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|    set   |   0 |   1 |   0 |   0 |   1 |   0 |
+----------+-----+-----+-----+-----+-----+-----+

->這是第5趟循環,尋找更小權值的邊:
->發現比min = 65535 更小的權值:6,修改min爲該值!
->發現比min = 6 更小的權值:5,修改min爲該值!

->這是第5趟循環,尋找更小權值的邊:
->發現比min = 65535 更小的權值:6,修改min爲該值!
->發現比min = 6 更小的權值:5,修改min爲該值!
->找到一條最小生成樹的新邊:2 <-- 5 --> 3
->合併頂點1到頂點2的集合中,注意set數組的變化。
->合併頂點3到頂點2的集合中,注意set數組的變化。
->合併頂點4到頂點2的集合中,注意set數組的變化。
->合併頂點6到頂點2的集合中,注意set數組的變化。
->set數組修改後的值:
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|    set   |   1 |   1 |   1 |   1 |   1 |   1 |
+----------+-----+-----+-----+-----+-----+-----+
->set數組初最終狀態(無向連通網所有頂點應該都在一個集合裏):
+----------+-----+-----+-----+-----+-----+-----+
|     i    |   0 |   1 |   2 |   3 |   4 |   5 |
+----------+-----+-----+-----+-----+-----+-----+
|   頂點   |   1 |   2 |   3 |   4 |   5 |   6 |
+----------+-----+-----+-----+-----+-----+-----+
|    set   |   1 |   1 |   1 |   1 |   1 |   1 |
+----------+-----+-----+-----+-----+-----+-----+

->測試銷燬圖: 成功!
演示結束,程序退出!

--------------------------------
Process exited with return value 0
Press any key to continue . . .

總結:
普利姆算法主要針對的是頂點,與圖中的邊數無關,適合求稠密網(邊比較多的網)的最小生成樹。
克魯斯卡爾主要針對邊,與圖中頂點數無關,適合求稀疏網(邊較少的網)的最小生成樹

下次的文章會介紹最短路徑兩種算法的實現,感謝大家一直以來的關注。再見!

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