Calmsym学习数据结构与算法之--并查集

文章目录

并查集

文章目录


前言

数据结构与算法是每一个CS人的必经之路

路漫漫其修远兮,今天总结的是关于数据结构的并查集


一、并查集是什么?

并查集(Disjoint-Set)是一种可以动态维护若干个不重叠的集合,并支持合并于查询的数据结构。

它被众多OIer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题。

用一个形象的比喻,并查集就像是一个老大,带着一堆小弟,并且不断招收新的小弟。这种”拉帮结派“的比喻就是并查集最核心的思想”代表元“。即每个集合选择一个固定的元素作为整个集合的”代表“,这是集合的表示方法,除此之外,我们还需要定义归属关系的表示方法。

->定义集合表示

->定义归属关系(如何维护)

二、原理及实现

1.如何定义

思路1. 维护一个数组f,用f[x]保存元素x所在集合的代表。

        这种方法可以达到O(1)的查询速度,但是在合并时需要大量修改元素的 f 值,效率低下

思路2. 使用树形结构储存每个集合。

        在一棵树中,树的节点就可以充当集合中的元素,树的根即为集合元素的代表。所以并查集实际是一片森林。

        我们可以通过维护数组fa来记录这片森林(多个集合),fa[x]保存x的父节点,特别的树根的父节点为他本身(代表)

2.代码实现

        并查集包括如下两个操作:

        1.Get 查询一个元素属于哪个集合

        2.Merge 把两个集合合并

代码如下:

int fa[N];
inline void init(int n){//初始化操作
     for(int i=1;i<=n;i++) fa[i]=i;
     //将每个元素的代表先设置为自己,为之后合并做初始化
}
inline int find(int x){
     if(fa[x]==x) return x;
     else return find(fa[x]);
     //我们用递归的写法实现对代表元素的查询
     //一层一层访问父节点,直至根节点(根节点的标志就是父节点是本身)。
     //要判断两个元素是否属于同一个集合,只需要看它们的根节点是否相同即可。
}
inline void merge(int x,int y){
     fa[find(x)]=find(y);
     //最易理解的,找到两个集合的代表元素,然后将前者的父节点设为后者即可
}

3.路径压缩与按秩合并

我们知道如果直接保存fa[x]保存代表,在查询时有很高的效率

实际上我们只关心每个集合对应的树形结构的根节点是什么,所以我们想尽可能减少树的高度,以达到更高的查询效率,即我们不关心树长什么样,最好情况是一个根节点连着无数的子节点,而子节点没有子子节点,例如下面两种图,在并查集中是等价的,但是右树的查询x的效率明显高于左树

所以我们可以在每次执行get操作时都把访问过的节点直接指向树根。这种优化方式被称为路径压缩

代码如下:

inline int get(int x){
     if(fa[x]==x) return x;
     return fa[x]=get(fa[x]);//路径压缩,fa直接赋值为代表元素
}

采用路径压缩的并查集,每次get查询的均摊复杂度为O(logN)

还有一种优化方法叫做按秩合并

对于秩有两种定义,一时某集合代表树的深度,二时该集合的大小。

但无论如何,该操作都是想实现--让小的集合直接记录在大集合的代表元上,即把小树直接插在大树的树根上,来实现最终查询尽可能快的结果


总结

对于并查集还有很多的拓展,诸如

”拓展域“与”边带权“的并查集

最小生成树下的并查集作用

在此暂不作整理,后续在学习最小生成树或图论更深层学习中再做强调

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值