连通图的一棵生成树是包含图的所有顶点的连通无环子图。
加权连通图的一棵最小生成树是图的一棵权重最小的生成树,其中,树的权重定义为所有边的权重总和。
最小生成树问题就是求一个给定的加权连通图的最小生成树问题。
最小生成树的算法主要有prim算法和kruskal算法,这篇主要讲解和实现后者。
kruskal算法主要是基于并查集+贪心的算法。该算法开始的时候,会按照权重的非递减顺序对所有的边进行排序,然后从一个空子图开始,扫描这个有序列表,并试图将列表中的下一条边加到当前的子图中。但是,如果加上这条边产生了回路,则把这条边跳过。
#include <iostream> #include <algorithm> using namespace std; const int N=150; int father[N],r[N],u[N],v[N],w[N];//定义father为每个节点的父亲,r为每条节点的编号,u,v为节点,w为边的权重 int find(int x)//并查集的核心算法,找寻父亲节点 { int r=x; while(father[r]!=r) { r=father[r]; } return r; } bool cmp(const int a,const int b)//主要用于sort函数中对于边的权重的排序 { return w[a]<w[b]; } int kruskal(int n,int m)//核心算法 { int mst=0,cnt=0; for(int i=0;i<n;i++)//初始化每个节点的父亲为自己本身 { father[i]=i; } for(int i=0;i<m;i++)//记录每条边的序号 { r[i]=i; } sort(r,r+m,cmp);//将边按照权重从小到大排序,这里只改变序号,就可以知道是哪条边 for(int i=0;i<m;i++)//对每条边来说,都判断边的两个顶点是否连通,如果连通,则证明加入之后,会出现回路,不予考虑 { int e=r[i]; int x=find(u[e]); int y=find(v[e]); if(x!=y) { mst+=w[e]; father[x]=y; cnt++; } } if(cnt<n-1)//证明此图不连通,不存在最小生成树 mst=0; return mst; } int main() { int n,m; cin>>n>>m; for(int i=0;i<m;i++) { cin>>u[i]>>v[i]>>w[i]; } int mst=kruskal(n,m); if(mst==0) cout<<"the mst is not found"<<endl; else cout<<"the mst is "<<mst<<endl; return 0; }