1. 程式人生 > >最小生成樹(Prim算法和Kruskal算法)

最小生成樹(Prim算法和Kruskal算法)

under net 任務 合並 一個 心算 std fin details

1)最小生成樹

給定一個無向圖,如果它的某個子圖中任意兩個頂點都互相連通並且是一棵樹,那麽這棵樹就叫生成樹。如果邊上有權值,那麽使得邊權和最小的生成樹叫做最小生成樹(MST,Minimum Spanning Tree)

2)應用

比如讓你為一個鎮的九個村莊架設通信網絡,每個村莊相當於一個頂點,權值是村與村之間可通達的直線距離,要求你必須用最小的成本完成這次任務;或者村莊之間建公路,連通N個村莊要N-1條路,如何讓建路成本最低之類的問題。

1、Prim算法

①該算法是構建最小生成樹的算法之一。它是以某頂點為起點,不斷添加各頂點上最小權值的邊,來構建最小生成樹。

②算法流程:

第一步:設圖G頂點集合為U,首先任意選擇圖G中的一點作為起始點a,將該點加入集合V;

第二步:從集合U中找到另一點b使得點b到V中任意一點的權值最小,此時將b點也加入集合V;

以此類推,現在的集合V={a,b},再從集合U中找到另一點c使得點c到V中任意一點的權值最小,此時將c點加入集合V;直至所有頂點全部被加入V,此時就構建出了一棵MST。

③參考代碼:

#include <iostream>
#include <string.h>
#define INT_MAX 1000000000
using namespace std;

int matrix[100][100];//鄰接矩陣
bool visited[100];//標記數組
int path[100];//記錄生成樹路徑
int lowcost[100
];//邊的權值 int vertex_num,arc_num;//頂點數,弧數 int sum;//權值總和 int source;//源點 void prim(int source); int main() { cout << "請輸入圖的頂點數(<=100):"; cin >> vertex_num; cout << "請輸入圖的弧數:"; cin >> arc_num; for (int i = 0; i < vertex_num; i++) for (int j = 0; j < vertex_num; j++) matrix[i][j]
= INT_MAX; //初始化matrix數組 cout << "請輸入弧的信息:\n"; int u, v, w; for (int i = 0; i < arc_num; i++) { cin >> u >> v >> w; matrix[u][v] = matrix[v][u] = w; } cout << "請輸入起點(<" << vertex_num << "):"; cin >> source; prim(source); cout << "最小生成樹權和為:" << sum << endl; cout << "最小生成樹路徑為:\n"; for (int i = 0; i < vertex_num; i++) if (i != source) cout << i << "----" << path[i] << endl; return 0; } void prim(int source){ memset(visited,0,sizeof(visited)); visited[source]=true; for(int i=0;i<vertex_num;i++){ lowcost[i] = matrix[source][i]; path[i]=source; } int min_cost,min_cost_index;//最小的權值,和其下標 sum=0; for(int i=1;i<vertex_num;i++){//找除源點外的n-1個點,如果這裏寫多了, //那麽下邊的for裏if進不去,那麽sum的值也會錯誤 min_cost=INT_MAX; for(int j=0;j<vertex_num;j++){//遍歷所有頂點 if(visited[j]==false&&lowcost[j]<min_cost){//找到與源點的權值最小的點 min_cost=lowcost[j]; min_cost_index=j; } } visited[min_cost_index]=true;//講找到的頂點進行標記 sum+=min_cost;//權值總和 for(int j=0;j<vertex_num;j++){ if(visited[j]==false&&matrix[min_cost_index][j]<lowcost[j]){//更新lowcost,以便找下個頂點 lowcost[j]=matrix[min_cost_index][j]; path[j] = min_cost_index; } } } }

摘自http://www.61mon.com/index.php/archives/199/comment-page-1#comments

2、Kruskal算法

①該算法是構建最小生成樹的另一個算法,其是對邊進行操作,來構建最小生成樹的。

②算法流程:

第一步:把所有的邊按權值從小到大排列。

第二步:按照順序選取每條邊,如果這條邊的兩個端點不屬於同一集合,那麽就將它們合並。

重復第二步,直到 所有的點都屬於同一個集合。     

第二步用並查集來實現,又因為每次都選的權值最小的,所以這其實就是運用並查集的貪心算法。

③為什麽這樣做就可以構建最小生成樹呢?因為N個頂點只需要N-1條邊就夠了,那麽選哪N-1條呢,為了讓權值和最小,當然選從小到大排序的前N-1條邊嘍。

④參考代碼:

#include <iostream>
#include <algorithm>
#define MAX_INT 100
using namespace std;
struct Edge{//
int a;//邊上的兩個點
int b;
int weight;//權值
}edge[MAX_INT];
int par[MAX_INT];//i的父親的編號
int rank[MAX_INT];//i的高度
void init(int n);//初始化
int find(int x);//查找根節點並且壓縮路徑
bool  unite(int x,int y);//合並
bool compare(const Edge&a,const Edge&b);
int vertex_num,arc_num;//頂點數,弧數
int case_num;//測試組數
int sum;//權值總和
int main()
{
    cout<<"請輸入測試組數:"<<endl;
    cin>>case_num;
    while(case_num){
        sum=0;
        cout<<"請輸入頂點數和弧數:"<<endl;
        cin>>vertex_num>>arc_num;

        init(vertex_num);

        cout<<"請輸入"<<arc_num<<"條弧的信息:"<<endl;
        for(int i=0;i<arc_num;i++){
            cin>>edge[i].a>>edge[i].b>>edge[i].weight;
            }

        sort(edge,edge+arc_num,compare);

        cout<<"最小生成樹的路徑為:"<<endl;
        int j;
        for(j=0;j<arc_num;j++){
            if(unite(edge[j].a,edge[j].b)){
                sum+=edge[j].weight;
                cout<<edge[j].a<<"----"<<edge[j].b<<" "<<edge[j].weight<<endl;
        }
        }
        if(j==arc_num){
            cout<<"最小生成樹的權值和為:"<<sum<<endl;
        }else if(j<arc_num){
            cout<<"data error!"<<endl;
        }

        case_num--;
    }
    return 0;
}
void init(int n){
    for(int i=0;i<n;i++){
        par[i]=i;//父節點都先初始化為自己
        rank[i]=0;//高度初始化為0
    }

}

int find(int x){
if(x==par[x]){
    return x;
}else{
    return par[x]=find(par[x]);
}
}
bool  unite(int x,int y){
    x=find(x);
    y=find(y);
    if(x==y){
        return false;
    }else{
    if(rank[x]<rank[y]){
        par[x]=y;
    }else{
        par[y]=x;
        if(rank[x]==rank[y])
            rank[x]++;
    }
    return true;
    }

}

bool compare(const Edge&a,const Edge&b){//按從小到大的順序排序
    return a.weight<b.weight;
}

代碼參考自:http://blog.csdn.net/niushuai666/article/details/6689285

3、Kruskal算法和Prim算法的區別(或者說什麽時候用Prim什麽時候用Kruskal)?

關於這個問題,我也不是很明白,後續會補充,歡迎在評論區寫下自己對此的理解,一起來討論下吧??

最小生成樹(Prim算法和Kruskal算法)