• 算法详解 【并查集】


    并查集概述

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

    • 缺点:对于每一个集合,只有一个“群主”,其他的都是 “群员”,群员之间没有层级之分。
    • ​ 对于每一个点 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:赋值运算符函数
    面试题:寻找热门查询
    面试题9:斐波那契数列
    Java中的volatile关键字
    二分查找算法
    面试题8:旋转数组的最小数字
    面试题:在O(1)空间复杂度范围内对一个数组中前后连段有序数组进行归并排序
    百度面试题:从海量日志中提取访问百度次数最多的IP
    面试总结
    java垃圾回收
  • 原文地址:https://www.cnblogs.com/betternow/p/13383374.html
Copyright © 2020-2023  润新知