Prim+Kruscal算法的C++实现
Kruscal原理:
使用贪心算法。先将所有边按照权值从小到大排序,每个点初始都是一个树(一个节点的树)。从前到后遍历边集,对于当前边x-y来说,如果x、y已经在我们的最小生成树里,那么跳过该边。
如果x、y至少有一个不在我们的最小生成树里,将该边加入最小生成树,并且将包含x的树和包含y的树合并为一棵树。(使用并差集的合并操作完成)
Kruscal算法适用于稀疏图
注:代码未验证正确性!
//Kruscal算法 2020年3月29日 21:41:19 //---------------------------------- vector<int> father; //并差集 void merge(int x,int y){ int x_father=get_father(x),y_father=get_father(y); father[x]=y_father; } int get_father(int x){ if(father[x]!=x){ return father[x]=get_father(father[x]); } return x; } //----------------------------------- struct edge //有权边 { int point1; int point2; int weigh; edge(int x,int y,int z):point1(x),point2(y),weigh(z){} }; //----------------------------------- //求带权无向图的MST边权和 int Kruscal(){ int points;//points:点数 cout<<"输入点个数:"<<endl; cin>>points; father.resize(points); for(int i=0;i<points;++i){ father[i]=i; } vector<edge> edges; //edges:有权边的集合 int r,s,t; while(cin>>r>>s>>t){ edges.emplace_back(r,s,t); } sort(edges.begin(),edges.end(),[](const edge& a,const edge& b){return a.weigh<b.weigh;}); //按照权从小到大排序 int res; //MST的权和 int edge_cnt=0; //已经选入的边数 for(const auto& edge:edges){ int p1=edge.point1,p2=edge.point2; if(father[p1]==father[p2]){//p1、p2已经加入MST continue; } res+=edge.weigh;//该边加入MST ++edge_cnt; merge(p1,p2);//合并两个点 if(edge_cnt>=points-1){//边数为点数-1时,完成了MST的构建 break; } } return res; }
Prim原理:
设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V,再从集合U-V中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;以此类推,现在的集合V={a,b},再从集合U-V中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V,直至所有顶点全部被加入V,此时就构建出了一颗MST。因为有N个顶点,所以该MST就有N-1条边,每一次向集合V中加入一个点,就意味着找到一条MST的边。
注:代码未验证正确性!
下面代码未使用优先队列进行优化,时间复杂度为|V|*|V|,可以优化为|V|*log|V|+|E|*lg|V|
//Prim算法 2020年3月29日 22:49:14 int Prim(){ int points;//points:点数 cout<<"输入点个数:"<<endl; cin>>points; vector<vector<int>> matrix(points,vector<int>(points,INT32_MAX)); //邻接矩阵,matrix[i][j]不为0:i到j有边相连,权为matrix[i][j] int r,s,t; while(cin>>r>>s>>t){ matrix[r][s]=t; matrix[s][r]=t; } int res=0; //最小生成树的边权和 vector<int> min_weigh(points,0); for(int i=1;i<points;++i){ min_weigh[i]=matrix[0][i]; } for(int i=0;i<points-1;++i){//循环points-2次 int min_distance=INT32_MAX; int which_point; //选出一条拥有最小权值的边,该边满足:一端在已找到的MST内,一端不在 for(int j=0;j<points;++j){ if(min_weigh[j] and min_weigh[j]<min_distance){ min_distance=min_weigh[j]; which_point=j; } } res+=min_distance; min_weigh[which_point]=0;//把新找到的点加入MST //对min_weigh数组进行更新,可能从which_point出发会有更小权的满足要求的边 for(int j=0;j<points;++j){ if(matrix[which_point][j]<min_weigh[j]){ min_weigh[j]=matrix[which_point][j]; } } }return res; }
进击的小🐴农