浅谈并查集

Posted xyqwq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈并查集相关的知识,希望对你有一定的参考价值。

本文将介绍并查集的模板以及各类问题中的应用

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

并查集可以算是很基础的结构了,可以快速查找一个元素在哪个集合中,算法模板大致为三大块:
1.Set(初始化)
2.Find(找到元素的祖先)
3.Union(合并两个集合)
因为Union是C艹关键字所以代码中以Merge代替


1.Set
建立新集合,各集合初始时不相交。如果用fa[]数组记录某个元素的父亲,即(f_i=j) i的父亲为j,那么初始化时每个元素的父亲指向自己,也就是说有(n)个集合

    void Set(int n)
    {
        for(int i=1; i<=n; ++i)
            fa[i]=i;
        return ;
    }

2.Find

技术分享图片

查找元素(x)的根节点,也就是不断向上找爸爸,直到寻找到某个元素(root)的父亲指向自己时,(root)就是这个集合的根。即(root)找不到爸爸了,它便是(x)所在集合的祖宗。
可是如果集合内元素很多,并且以链状排列(如上左图),每一次都递归找祖宗很浪费时间,我们就需要路径压缩了(如上右图)。

所谓路径压缩,就是在Find的查找路径上把每个节点都直接指向根节点,这样下次再找根节点的时间复杂度会变成O(1):

    int Find(int x)
    {
        if(fa[x] == x) return x;
        return fa[x]=(Find(fa[x])); //不断更新元素x的爸爸,同时向上寻找祖宗
    }

3.Union

技术分享图片

把两个集合合并在一起,我们可以将一个集合的祖宗的父亲指向另外一个集合的祖宗,此时集合数减一

    void Merge(int x, int y)
    {
        int root1=Find(x), root2=Find(y);
        if(root1 == root2) return ; //如果是一个集合就不用合并
        fa[root1]=root2; return ;
    }

$ $

那么并查集的模板就差不多长这样:

namespace Union_Find
{
    void Set(int n)
    {
        for(int i=1; i<=n; ++i)
            fa[i]=i;
        return ;
    }
    int Find(int x)
    {
        if(fa[x] == x) return x;
        return fa[x]=(Find(fa[x]));
    }
    void Merge(int x, int y)
    {
        int root1=Find(x), root2=Find(y);
        if(root1 == root2) return ;
        fa[root1]=root2; return ;
    }
}

using namespace Union_Find;

模板例题:
并查集模板
亲戚

以上是关于浅谈并查集的主要内容,如果未能解决你的问题,请参考以下文章

浅谈并查基

并查集模板

并查集浅谈及其扁平化

浅谈并小结一下web前端的知识点

树的应用——并查集及实现代码

关于并查集的一切全在这里了