并查集深入浅出________Almost Union-Find

并查集概述

针对散列点的集合操作,将哪些点集合到一起,判定哪些点在一个集合是并查集的基本任务。

缺点:对于每一个集合,只有一个“群主”,其他的都是 “群员”,群员之间没有层级之分。

           对于每一个点 i 判定给定一个 pre[ i ];若两个点的 pre[ i ] 相等则属于一个集合,其中 pre[ i ] = i 的点为“群主”。除此之外,若有 pre[ i ] = pre[ pre[ j ] ],形成类似链式关系 , 则递归查找时将 pre[ j ] = pre[ i ],将子点全都归于“群主” 。 

注意:为了精确的找到某个群员的群主,并且将群员的 pre 全都对齐于群主,我们一般采用 Find( i ) 代替 pre[ i ]

并查集基本操作

初始化

对于每一个点,它一个人属于一个集合,它是它自己的“群主”。

        void init()
        {
            for(int i=1;i<=n;i++){
                pre[i] = i;
            }
        }

查找

对于每一个点,递归查找它的 pre[  ] , 直到最后满足 pre[ i ]  = i

        int Find(int x)
        {
            if(x == pre[x]) return x;
            return pre[x] = Find(pre[x]);
        }

归并

        void Union(int x,int y)
        {
            int a = Find(id[x]),b = Find(id[y]);
            if(a == b) return;
            pre[a] = b;
        }

Almost Union-Find

Almost Union-Find,并查集+删除节点

在并查集中删除一个节点是很麻烦的事情,所以我们引入一个新的东西,用 id[ i ] 代替原有的 i 进行并查集操作,如果有一个点我们想把它从某一个集合中拿出来,就重新给 i 定义一个新的 id[ i ]并将它的状态初始化,一般用(++n)。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int pre[maxn],id[maxn],num[maxn],sum[maxn];
int n,m;
void init()
{
    for(int i=1;i<=n;i++){
        pre[i]=id[i]=sum[i]= i;
        num[i]=1;
    }
}
int Find(int x)
{
    if(x == pre[x]) return x;
    return pre[x] = Find(pre[x]);
}
void Union(int x,int y)
{
    int a = Find(id[x]),b = Find(id[y]);
    if(a == b) return;
    pre[a] = b;
    num[b] += num[a];
    sum[b] += sum[a];
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        int op,x,y;
        for(int i=1; i<=m; i++)
        {
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d",&x,&y);
                Union(x,y);
            }
            else if(op==2){
                scanf("%d%d",&x,&y);
                if(Find(id[x]) != Find(id[y])){
                    num[Find(id[x])]--;
                    sum[Find(id[x])]-=x;
                    id[x] = ++n;
                    pre[id[x]] = id[x];
                    num[id[x]] = 1;
                    sum[id[x]] = x;
                    Union(x,y);
                }
            }
            else if(op==3){
                scanf("%d",&x);
                printf("%d %d\n",num[Find(id[x])],sum[Find(id[x])]);
            }
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值