本日主要内容是并查集和堆。
- 并查集
并查集是一种树型的数据结构,通常用来处理不同集合间的元素之间的合并与查找问题。一个并查集支持三个基本功能:合并、查找和判断。举一个通俗的例子,我和lhz认识,lhz和hzc认识,那么也就可以断定我和hzc认识。
依照并查集的思想,我们把所有要待处理的元素a,a,a....a这n个元素都看作是一个单独的集合,初始状态每个集合都只有一个元素。我们就可以把并查集的合并操作理解为集合之间的取并集操作。
作为一个树形结构,在一个由许多这样的集合构成的森林中,每个集合都应该有它自己的「代表元素」,即能够代表一整个集合的所有元素的元素。可以这样理解,在一个地方存在着许多黑恶势力,而每个黑恶势力都有一个自己的头目,这个头目就是集合里的「代表元素」。对于并查集,每个集合选择哪个元素作为代表元素不是我们要关心的问题,但是我们要保证这个代表元素要在集合不发生改变的状态下是不变的,换句话说,不能随便换头目。
那么这应该怎么操作呢?
对于一棵树,我们通常使用一个father数组记录某点的父亲节点,即用father[i]表示第i号点的父亲节点是谁。但是对于并查集,这个father数组则就是用来记录第i个点所在的集合的「代表元素」。
则初始化一个并查集的代码就明确了。
for (int i=;i<=n;i++)
father[i] = i;
下面介绍查找代表元素的find操作。find的作用是查找一个节点x所在集合的代表元素。
int find(int x){
if (father[x] == x)
return x;
else
return find(father[x]);
}
它使用了递归。可以想象一个暴力爬树的过程,如果我现在在i号节点,我为了要找到我们这个集合的代表元素,我肯定要沿着我的father向上爬,直到我找到一个元素,它的father是它本身,那么这就一定是那个黑恶势力的头目了。
时间复杂度O(h),h代表这个点距离代表元素的高度。
*路径压缩
我们考虑极端情况。如果有一个集合的某个链非常非常长,而我们要找到这个集合最下边的代表元素的话,是需要O(h)时间的,这个h有可能会非常大,如果再这样使用暴力爬树的方法就会吃到一个TLE。
不怕,我们有路径压缩!
路径压缩的思想非常简单,我在find的时候不是要不断地找祖先吗,那如果所有节点都几乎直接插在代表元素节点不远处,查找的次数不就大大变少了?说的再通俗一点,就是把一条路上的节点的father全部更新成真正的代表元素,而不是合并之前的代表元素,这就相当于是直接把这个地方插到代表元素上,所以就大大减少的查找的递归次数。
还是听不懂?Shut up and take my code!
int find(int x){
if (father[x] == x)
return father[x];
father[x] = find(father[x]);//如果当前节点的father并不是代表元素,那就递归地更新老祖宗
return father[x];//返回老祖宗
}
这个函数的时间复杂度是O(α(n)),α(n)代表反阿克曼函数,反阿克曼函数时一个增长非常缓慢的函数,通常来说,反阿克曼函数的最大值不会超过4,所以路径压缩的find完全可以看作是一个O(1)的常数操作。
各位读者可以尝试画一下图,可以发现,路径压缩之后的并查集是“类菊花形”的。
合并集合:只需要把一个头目变成另一个头目的下属就可以了。
void merge(int x,int y){
x = find(x);
y = find(y);
father[y] = x;
}
判断两个元素是不是在同一集合,只需要问问他们的头目是谁就知道了。
bool check(int x,int y){
x = find(x);
y = find(y);
if (x==y)
return true;
else
return false;
}
很简单吧,是不是?
并查集的应用:kruskal算法求图上的最小生成树。
这里我先卖个关子,等我写到Day3 图论时再去详细介绍这个算法,它并不难理解。
例题:
T1:程序自动分析
luoguP1955.
离散化+并查集可做。所谓离散化就是把输入时相同的内容都去掉,可以用map,hash或者unique函数等等操作……我应该会在后面的一篇随笔上详细的介绍离散化操作,这里只是给一个粗略的思想。对于这个题我们可以用并查集维护所有数之间的相等关系。先处理所有的等式,将等式两边的两个数所在的集合合并在一起。然后我们检查所有的不等式。如果某个不等式两边的数字在一个集合中(被要求相等),则输出NO。不存在这样的情况则输出YES。
用两个并查集分别记录相等和不等关系好像也是可以的。
此外,这道题在Day1上午的模拟赛上也出现了。
T2:Connect
(我在luogu上找不到这题)
给定一个点数为
05-11 19:31