算法详解 【并查集】

并查集概述

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

  • 缺点:对于每一个集合,只有一个“群主”,其他的都是 “群员”,群员之间没有层级之分。
  • ​ 对于每一个点 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;
}
posted @ 2020-07-27 02:34  蔺昌黎  阅读(187)  评论(0编辑  收藏  举报