最小生成樹--兩種常見的寫法;

普利姆最小生成樹算法

普里姆算法(Prim算法),圖論中的一種算法,可在加權連通圖裏搜索最小生成樹。意即由此算法搜索到的邊子集所構成的樹中,不但包括了連通圖裏的所有頂點(英語:Vertex (graph theory)),且其所有邊的權值之和亦爲最小。

算法描述

1).輸入:一個加權連通圖,其中頂點集合爲V,邊集合爲E;
2).初始化:Vnew = {x},其中x爲集合V中的任一節點(起始點),Enew = {},爲空;
3).重複下列操作,直到Vnew = V:
a.在集合E中選取權值最小的邊<u, v>,其中u爲集合Vnew中的元素,而v不在Vnew集合當中,並且v∈V(如果存在有多條滿足前述條件即具有相同權值的邊,則可任意選取其中之一);
b.將v加入集合Vnew中,將<u, v>邊加入集合Enew中;
4).輸出:使用集合Vnew和Enew來描述所得到的最小生成樹

圖解:

初始狀態:V是所有頂點的集合,即V={A,B,C,D,E,F,G};U和T都是空! 
第1步:將頂點A加入到U中。 
    此時,U={A}。 
第2步:將頂點B加入到U中。 
    上一步操作之後,U={A}, V-U={B,C,D,E,F,G};因此,邊(A,B)的權值最小。將頂點B添加到U中;此時,U={A,B}。 
第3步:將頂點F加入到U中。 
    上一步操作之後,U={A,B}, V-U={C,D,E,F,G};因此,邊(B,F)的權值最小。將頂點F添加到U中;此時,U={A,B,F}。 
第4步:將頂點E加入到U中。 
    上一步操作之後,U={A,B,F}, V-U={C,D,E,G};因此,邊(F,E)的權值最小。將頂點E添加到U中;此時,U={A,B,F,E}。 
第5步:將頂點D加入到U中。 
    上一步操作之後,U={A,B,F,E}, V-U={C,D,G};因此,邊(E,D)的權值最小。將頂點D添加到U中;此時,U={A,B,F,E,D}。 
第6步:將頂點C加入到U中。 
    上一步操作之後,U={A,B,F,E,D}, V-U={C,G};因此,邊(D,C)的權值最小。將頂點C添加到U中;此時,U={A,B,F,E,D,C}。 
第7步:將頂點G加入到U中。 
    上一步操作之後,U={A,B,F,E,D,C}, V-U={G};因此,邊(E,G)的權值最小。將頂點G添加到U中;此時,U=V。

此時,最小生成樹構造完成!它包括的頂點依次是:A B F E D C G


1、直接就是進行上面的操作過程;

#include <iostream>
#include <cstring>
#define INF 1<<3
#define NUM 100


using namespace std;
int dot_num;//表示頂點數;
int dot_lines;//表示邊數;
int Graph[NUM][NUM];//存放邊的權值(數組法)
bool visted[NUM] ={false};
struct VNode{
    int adjvex;
    int lowcost;
}minside[NUM];//存放adjvex這個樹內和樹外連接頂點之間的最小的所有權值;



int min_xu()
{
    int i = 1;
    int k;
    while(!minside[i].lowcost&&i<=dot_num)
        i++;//找出第一個非零的值
    k = i;//獲取這個的位置
    int min_value = minside[i].lowcost;//令這個位置的這個非零值爲最小的值;
    for(int j = i;j<=dot_num;j++)
        if(min_value>minside[j].lowcost&& minside[j].lowcost>0)
            {min_value = minside[j].lowcost;k = j;}//獲取最小的值;
    return k;
}





void MiniSpanTree(int v)
{
    int k;
    for(int i  = 1;i<=dot_num;i++){
        minside[i].adjvex = v+1;
        minside[i].lowcost = Graph[v+1][i];
    }
    minside[v+1].lowcost = 0;
    for(int i = 2;i<=dot_num;i++){
        k = min_xu();
        cout <<minside[k].adjvex<<"V->V"<<k<<endl;
        minside[k].lowcost = 0;
        for(int i = 1;i<=dot_num;i++)
            if(minside[i].lowcost>Graph[k][i]){//更新在樹外到樹內的所有的權值;
                minside[i].adjvex = k;
                minside[i].lowcost = Graph[k][i];
            }
    }
}




int main()
{
    cin>>dot_num>>dot_lines;
    memset(Graph, 127, sizeof(Graph));
    for(int i = 0;i<dot_lines;i++){
        int temp1,temp2,value;
        cin>>temp1>>temp2>>value;
        Graph[temp2][temp1]=value;
        Graph[temp1][temp2]=value;
    }

    MiniSpanTree(0);
    return 0;
}


克魯斯卡爾算法


克魯斯卡爾算法的時間複雜度爲O(eloge)(e爲網中邊的數目),因此它相對於普里姆算法而言,適合於求邊稀疏的網的最小生成樹
克魯斯卡爾算法從另一途徑求網的最小生成樹。假設連通網N=(V,{E}),則令最小生成樹的初始狀態爲只有n個頂點而無邊的非連通圖T=(V,{∮}),圖中每個頂點自成一個連通分量。在E中選擇代價最小的邊,若該邊依附的頂點落在T中不同的連通分量上,則將此邊加入到T中,否則捨去此邊而選擇下一條代價最小的邊。依次類推,直至T中所有頂點都在同一連通分量上爲止。
例如圖爲依照克魯斯卡爾算法構造一棵最小生成樹的過程。代價分別爲1,2,3,4的四條邊由於滿足上述條件,則先後被加入到T中,代價爲5的兩條邊(1,4)和(3,4)被捨去。因爲它們依附的兩頂點在同一連通分量上,它們若加入T中,則會使T中產生迴路,而下一條代價(=5)最小的邊(2,3)聯結兩個連通分量,則可加入T。因此,構造成一棵最小生成樹
上述算法至多對 e條邊各掃描一次,假若以“堆”來存放網中的邊,則每次選擇最小代價的邊僅需O(loge)的時間(第一次需O(e))。又生成樹T的每個連通分量可看成是一個等價類,則構造T加入新的過程類似於求等價類的過程,由此可以以“樹與等價類”中介紹的 mfsettp類型來描述T,使構造T的過程僅需用O(eloge)的時間,由此,克魯斯卡爾算法的時間複雜度爲O(eloge)。
//克魯斯卡爾算法求無向連通網的最小生成樹的程序
#include <iostream>
#include <cstring>
#define NUM 100
#define INF 1<<3
#define MAX_VERTEX_NUM 100

using namespace std;
int dot_num;//表示頂點數;
int dot_lines;//表示邊數;
int G[NUM][NUM];//存放邊的權值(數組法)
bool visted[NUM] ={false};


struct VNode{
    int adjvex;
    int lowcost;
}minside[NUM];//存放adjvex這個樹內和樹外連接頂點之間的最小的所有權值;


void Kruskal()
{
    int set[MAX_VERTEX_NUM];


    for(int i=1; i<=dot_num; i++)
        set[i]=i;//初始化;//相當於存放每一個頂點;


    printf("最小代價生成樹的各條邊爲\n");
    int k=0;
    int a = 1;
    int b = 1;
    int min = 1<<30;//表示最小的權值;
    while(k<dot_num-1){//表示找出這個n-1條邊
        for(int i = 1;i<=dot_num;i++)
            for(int j = i;j<=dot_num;j++){
                if(G[i][j]<min){
                    min = G[i][j];
                    a = i;
                    b = j;
                }
            }//找到最小的權值;在所有的邊中。並且記錄其中的頂點是那兩個;
        min = G[a][b] = 1<<30;//更該其中的圖的邊的值,並且將這個值賦值給min
        if(set[a] !=set[b]){
            cout <<a<<"V->V"<<b<<endl;
            k++;
            int temp = set[b];
            for(int i = 1;i<=dot_num;i++)
                if(set[i] == temp)
                    set[i] = set[a];
        }
    }
}





int main()
{

    cin>>dot_num>>dot_lines;
    memset(G, 127, sizeof(G));
    for(int i = 0;i<dot_lines;i++){
        int temp1,temp2,value;
        cin>>temp1>>temp2>>value;
        G[temp2][temp1]=value;
        G[temp1][temp2]=value;
    }
    Kruskal();
}


測試數據:

6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6


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