Prim和Kruskal算法

对于一个边带有权值的图,我们可以求它的最小生成树(Minimum Spanning Tree)。通常有两种算法,Prim和Kruskal。

Prim算法的思路是每次找到距离当前生成树最近的一个结点,并将它连到当前的生成树。

代码如下:

 1 //Prim 
 2 #include <iostream>
 3 #include <cstring>
 4 #include <vector>
 5 #include <queue>
 6 using std::cin;
 7 using std::cout;
 8 using std::endl;
 9 using std::vector;
10 
11 const int INF = 0x3f3f3f3f;
12 
13 struct Edge {
14     int vertex, weight;
15 };
16 
17 class Graph {
18 private:
19     int n;
20     bool *visited;
21     vector<Edge> *edges;
22 public:
23     //dist stores the min distance of a node to the tree
24     int *dist;
25     Graph (int input_n) {
26         n = input_n;
27         edges = new vector<Edge>[n];
28         dist = new int[n];
29         visited = new bool[n];
30         memset(visited, false, n * sizeof(bool));
31         memset(dist, 0x3f, n * sizeof(int));
32     }
33     ~Graph() {
34         delete[] dist;
35         delete[] visited;
36         delete[] edges;
37     }
38     void insert(int x, int y, int weight) {
39         edges[x].push_back(Edge{y, weight});
40         edges[y].push_back(Edge{x, weight});
41     }
42     int prim(int v) {
43         int total_weight=0;
44         //v is the starting node
45         dist[v]=0;
46         //loop for n times, each time settle one node
47         for(int i=0;i<n;i++){
48             int min_dist=INF,min_vertex;
49             for(int j=0;j<n;j++){
50                 //choose the node with min dist to the tree
51                 if(!visited[j]&&dist[j]<min_dist){
52                     min_dist=dist[j];
53                     min_vertex=j;
54                 }
55             }
56             total_weight+=min_dist;
57             visited[min_vertex]=1;
58             //update the nodes connected to the min_vertex
59             //dist will store the min dist to the tree
60             for(Edge &j: edges[min_vertex]){
61                 if(!visited[j.vertex]&&j.weight<dist[j.vertex]){
62                     dist[j.vertex]=j.weight;
63                 }
64             }
65         }
66         return total_weight;
67     }
68 };

Kruskal的思路是每次找到剩余的权值最小的边,如果这条边与当前MST不构成环,则将这条边加入MST。利用并查集,我们可以很轻松地判断边是否会与MST构成环,因为如果边的两个顶点在并查集中属于同一棵树,那么这条边一定会形成环。

代码如下:

 1 //kruskal
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <vector>
 5 #include <set>
 6 using namespace std;
 7 
 8 //father of nodes
 9 int *father;
10 struct Edge{
11    int ver1,ver2,weight;
12    bool operator< (const Edge &e) const{
13        return weight<=e.weight;
14    }
15 };
16 vector<Edge> MST;
17 
18 void initialize(int size){
19    father=new int[size];
20    for(int i=0;i<size;i++){
21        father[i]=i;
22    }
23 }
24 
25 int find_root(int node){
26    if(father[node]!=node){
27        father[node]=find_root(father[node]);
28    }
29    return father[node];
30 }
31 
32 bool merge(int node1, int node2){
33    int ancestor1=find_root(node1);
34    int ancestor2=find_root(node2);
35    if(ancestor1!=ancestor2){
36        father[ancestor2]=ancestor1;
37        return true;
38    }
39    return false;
40 }
41 
42 int kruskal(int num_of_N, int num_of_E){
43    int ans=0;
44    initialize(num_of_N);
45    //边按权值从小到大排序
46    set<Edge> edges;
47    int v1,v2,weight;
48    //输入
49    for(int i=0;i<num_of_E;i++){
50        cin>>v1>>v2>>weight;
51        edges.insert({v1-1,v2-1,weight});
52    }
53    set<Edge>::iterator iter=edges.begin();
54    while(iter!=edges.end()){
55         //merge成功说明这两个结点原本不在一棵树上,不构成环
56         //因此将这条边加入MST
57        if(merge(iter->ver1, iter->ver2)){
58            MST.push_back(*iter);
59            ans+=iter->weight;
60        }
61        ++iter;
62    }
63    return ans;
64 }
65 
66 int main(){
67    int n,m;
68    cin>>n>>m;
69    int t=kruskal(n, m);
70    cout<<t<<endl;
71    vector<Edge>::iterator ite=MST.begin();
72    while(ite!=MST.end()){
73        cout<<ite->ver1<<"  "<<ite->ver2<<"   "<<ite->weight<<endl;
74        ite++;
75    }
76    delete[] father;
77 }

 

posted @ 2017-07-04 17:33  NoviScl  阅读(214)  评论(0编辑  收藏  举报