Today是本蒟蒻学习并查集的第一天,加油!
0x00 什么是并查集
并查集,顾名思义,就是用来进行合并和查找操作的数据结构。可以以较低的时间复杂度进行树的合并以及元素的查找。并查集可以用多种数据结构实现,在本文中使用数组。
0x01 初始化
这个灰常简单,a[i]
就初始化为i
0x02 查找
我们一层一层访问父节点,直到访问根节点,也就是a[i]=i
(因为根节点的值就是它本身)
0x03 合并
现在有两个元素,要合并他们,首先得知道他们分别的代表元素(根节点),将其中一个根节点赋值为后者的根节点即可
0x04 优化:路径压缩
我们考虑以下情况:
假如我们要查找5的话,就需要4次递归,那有没有一种方法,可以帮我们减少递归次数呢?
我们可以想象这样一个画面:
如果图是这样的话,我们仅需递归1次就可以找到5了。路径压缩就是在查询的过程中,把途中的每个节点的父节点都设为根节点,这样下次查找时直接就时根节点
0x05 优化:按秩合并
其实路径压缩已经可以应付大部分的题目了,除非卡的特别紧,才必须要用按秩合并
由于我们在找出一个元素所在集合的代表时需要递归地找出它所在的树的根结点,所以为了减短查找路径,在合并两棵树时要尽量使合并后的树的高度降低,所以要将高度低的树指向高度更高的那棵,这就是按秩合并的思想
在合并时,如果深度相同且根节点不同,则新的根节点的深度(秩)+1
0x06 小例题
Codeforces EDU Disjoint Sets Union Step1 A
简单题意:执行两种类型的查询:
1.union u v
合并包含元素 u 和 v 的两个集合;
2.get u v
检查元素 u 和 v 是否属于同一集合。
一共n
个元素,询问m
次,n,m
≤
1
0
5
\le 10^5
≤105
Solution:
这就是一道并查集的模板题,直接上核心代码:
class dsu{
public:
static inline void make(int x){for(int i=1;i<=x;i++)a[i]=i;}
static inline int find(int x){return x==a[x]?x:a[x]=find(a[x]);}
static inline void merge(int u,int v){
int i=find(u),j=find(v);
if(ran[i]<ran[j])a[i]=j;
else a[j]=i;
if(ran[i]==ran[j]&&i!=j)ran[j]++;
}
};
后记:
呜呜呜~~~当时把find函数写成了bool类型,结果在那傻傻的查了半个小时,一直不知道哪里错了,大家一定要引以为戒