最小生成树——Kruskal详解

最小生成树

一个有 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值