11.2最小生成树

11.2.1kruskal算法

第一步,是给所有边从小到大的顺序排列。这一步可以直接使用qsort,sort,接下来依次考查每条边(u,v)

情况1:u和v在同一个连通分量中,那么加入(u,v)后会形成环,因此不能选择

情况2:如果u和v在不同的连通分量,那么加入(u,v)一定是最优的

最关键的部分在于“连通分量的查询与合并”:需要知道任意两个点是否在同一个连通分量中,还需要合并两个连通分量

最容易想到的是暴力,然后暴力很浪费时间


并查集。

有一种简洁高效的方法可以处理这个问题:使用并查集,可以把每个连通分量看成一个集合,改集合包含了连通分量中的所有点,这些点两两连通,而具体的连通方式无关紧要,就好比集合中的所有元素没有先后顺序之分。在图中,每个点恰好属于一个连通分量,对应到集合表示中,每个元素恰好属于一个集合,换句话说,图中所有连通分量可以用若干个不相交集合表示

并查集的精妙之处在于用树来表示集合,例如,若包含点1,2,3,4,5,6的图有3个连通分量{1,3},{2,5,6},{4},则需要3个树表示。这3个树的具体形态无关紧要,只要一棵树包含1,3两个点,一棵树包含2,5,6,一棵树包含4.规定每颗树的根结点是这棵树所对应的集合代表有

如果把x的父结点保存在p[x]中,没有父结点等于本身。

改进:

既然每棵树表示的只是一个集合,因此树的形态无关紧要的,并不需要在“”操作之后保证形态不变,只要顺便把遍历过得结点改成树根的子节点


假设第i条边的两个端点的序号和权值分别保存在u[i],v[i],w[i]中。而排序后第i小的边的序号保存在r[i]中(叫做间接排序。排序的关键字是对象的“代号”,而不是对象本身)。

int cmp(const int i,const int j){
    return w[i]<w[j];
} //间接排序函数
int find(int x){
    return p[x]==x? x: p[x]=find(p[x]);  //并查集的find
}
int Kruskal(){
    int ans=0;
    for(int i=0;i<n;i++) p[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)
        {
            ans+=w[e];
            p[x]=y;
        }
    } 
    return ans;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值