前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并查集

并查集

作者头像
灯珑LoGin
发布2022-10-31 12:34:00
6570
发布2022-10-31 12:34:00
举报
文章被收录于专栏:龙进的专栏

并查集是一种用互质的集合对数据进行分类管理的数据结构。

并查集主要实现了两个功能:合并与查询

我们用一个数组fa[i]来表示第i个元素所在集合的根节点。

根节点的父节点指向它自身。

初始的时候,我们把fa[i]=i,这样就初始化了n个互质的集合。

然后当要合并两个节点x、y所在的集合的时候,就先找到他们的根节点(代表元),然后将一个集合的根节点指向另一个节点的根节点即可。

判断两个元素所在集合是否相同,其实就是去找他们所在集合的代表元。代表元相同,那么就证明这两个元素在同一个集合里面。

对于题目 DSL_1_A 来说,题目要求实现一个简单的并查集,代码如下:

代码语言:javascript
复制
#include<iostream>
#include<string.h>
using namespace std;

#define MAXN 10005

int fa[MAXN];
int n;

int find_root(int x)
{
    if(fa[x]==x)
        return x;
    
    return find_root(fa[x]);
    
}


void unite(int x,int y)
{
    fa[find_root(x)] = find_root(y);
}


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n;
    for(int i=0;i<n;++i)
    {
        fa[i] = i;
    }

    int q;
    cin>>q;

    int com,x,y;
    for(int i=0;i<q;++i)
    {
        cin>>com>>x>>y;
        if(com==0)
        {
            unite(x,y);
        }
        else
        {
            cout<<(find_root(x)==find_root(y)?1:0)<<endl;
        }
        
    }
    
}

对于上面这个题目,我们会发现,运行的状况是这样的

这虽然是AC了,可是耗时有点高啊,耗时0.4s,n的数据范围是10000,操作数最大为100000。这怎么看都不像一个时间复杂度低于O(logn)的算法啊。

那么肯定是有优化空间的。

路径压缩

但是,这样子的话,每次查找都需要递归很多次,非常的费时。我们就可以在合并集合的时候,做路径压缩来解决这个问题。

路径压缩就是,把一个集合里面,正在合并的节点都的父节点都指向合并时的根节点。这样使得路径得到了压缩,减少了查询的耗时。

怎么说吧,我觉得路径压缩有点动态规划的思想在里面,就是每次查询找到当前节点的根节点之后,就更新进去fa数组,然后下次用到这个值的时候,就可以减少调用的次数了。

代码实现如下:

代码语言:javascript
复制
int find_root(int x)
{
    if(fa[x]==x)
        return x;
    
    int t = find_root(fa[x]);
    fa[x] = t;
    return t;
    
}

按秩合并

并查集的按秩合并说白了就是把高度矮的树合并到高度高的树上

按秩合并能显著降低最长路径长度,这样的话,在查询的时候可以更快地查询到根节点。只有使用了路径压缩+按秩合并的并查集,时间复杂度才会低于O(logn)

我们需要使用一个数组Rank[i]来存储第i个节点作为根节点时,它的树的高度。

那么我们发现,需要更新Rank[i]的情况只有是在合并集合且两个集合树高相等时,才需要把一个集合的根节点的树高+1。

具体的代码实现就是,初始化一个全局数组Rank[i],把它的值都设为0,接着,把合并集合的函数改成下面这样:

代码语言:javascript
复制
void unite(int x,int y)
{
    int fx = find_root(x);
    int fy = find_root(y);

    if(Rank[fx]>Rank[fy])
    {
        fa[fy] = fx;
    }
    else
    {
        fa[fx] = fy;
        if(Rank[fx]==Rank[fy])
            Rank[fy]++;
    }

}

再把代码提交上去,可以发现,运行时间减少到了0.05s,这差不多加速了10倍啊!

带权并查集

带权并查集就是在并查集的树的连边上附上权值。

带权并查集的合并,需要把权值也加起来。

其实理解并不困难,就是用一个数组s[i],来存储当前节点到路径压缩后的父节点的权值和。查询的时候,进行路径压缩,并更新s[i]的值。

在合并的时候,需要对新的这条连边赋值,看下面这个图就知道了。

已知:C-A=z

那么根据初中数学知识就可以知道,D-B = y+z-x

给新建立的连边赋值就好了。

转载请注明出处:https://www.longjin666.top/?p=749

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年1月29日2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 路径压缩
  • 按秩合并
  • 带权并查集
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档