最小生成树
一个有 n 个结点的连通图的生成树是原图的最小连通子图,包含原图中的所有 n 个结点(意味着有n-1条边),能保持图连通并且权值和最小的边
Kruskal 求MST
复杂度:
O(E*logE),E为边数。适合于求边较少图的最小生成树
原理:
首先将所有的边按从小到大顺序排序,并认为每一个点都是孤立的,分属于n个独立的集合。然后按顺序枚举每一条边,如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去。直到选取了n-1条边为止。
图示
步骤:
-
初始化标记数组(father)
-
把图中的所有边按权值从小到大排序
-
把图中的n个顶点看成独立的n个集合
-
按权值从小到大遍历边,判断所选的边连接的两个节点e1,e2是否属于不同的集合,如果属于不同集合就成为最小生成树的一条边,并将这两个集合合并成一个集合。当生成了n-1条边后即可结束
PS:判断是否属于同一集合使用并查集
代码:
数组排序写法:
const int N=1e4+10;
struct node{
int from,to,v; //始边,终边,权值
}edge[N];
int father[N];
int n,m; //节点数和边数
bool cmp(node &a,node &b){
return a.v<b.v;
}
int Find(int x){
return father[x]==x?x:father[x]=Find(father[x]);
}
int Kruskal(){
for(int i=1;i<=n;i++) father[i]=i;
sort(edge+1,edge+1+m,cmp);
int ans=0,res=0;
for(int i=1;i<=m;i++){
if(res==n-1) break;
int fx=Find(edge[i].from);
int fy=Find(edge[i].to);
if(fx!=fy){
father[fy]=fx;
ans+=edge[i].v;
res++;
}
}
return ans;
}
优先队列写法:
const int maxn=?;
struct node{
int from,to,v;
bool operator < (const node &p) const {
return v>p.v;
}
}edge[maxn];
int father[maxn];
int n,m; //节点数和边数
priority_queue<node> p;
int Find(int x){
return father[x]==x?x:father[x]=Find(father[x]);
}
int Kruskal(){
for(int i=1;i<=n;i++)
father[i]=i;
for(int i=1;i<=m;i++) p.push(edge[i]);
int ans=0,res=0;
while(!p.empty()){
node u=p.top();
p.pop();
if(res==n-1) break;
int fx=Find(u.from);
int fy=Find(u.to);
if(fx!=fy){
father[fy]=fx;
ans+=u.v;
res++;
//printf("%d %d %d\n",u[i].from,u[i].to,u[i].v); //打印该边
}
}
return ans;
}