《极其巧妙的并查集》
处理有传递性关系的问题,可以使用「并查集」。发明并查集的人获得了图灵奖,也用并查集告诉了世人大道至简的真理。并查集的思路和代码极其简洁明了,但是却能让复杂的问题束手就擒。直观来说,并查集解决的问题是组团和配对的问题,判断两个个体是否在一个集合中(find),合并两个个体到一个集合(unite),以及并查集中有多少集合(count),解决的问题也相对比较固定,朋友圈,以图判树,岛屿个数…,而对于解决实际问题,也是非常得力的工具,比如我们在做猪脸识别自动建档的时候就用到了并查集。
Key Words:并查集、组团、配对
Beijing, 2020
并查集的逻辑
- init: 每个个体的parent都是其本身
- find: 查找个体的头目就是找到 x == parent[x] 的元素
- unite: 合并两个个体,先找到两个个体的集体头目,如果不在一个集体中,则合并两个集体头目即可
- count: 计算并查集中有多少个集体,就是查有多少个元素满足 x == parent[x]
最简单的并查集实现
class UnionFind
{
private:
vector<int> parent;
vector<int> rank; // 后面优化会用到
public:
UnionFind(int n):parent(n, -1)
{
for (int i = 0; i < n; ++i)
{
parent[i] = i;
}
}
void unite(int x, int y)
{
int px = find(x);
int py = find(y);
if (px != py)
{
parent[px] = py;
}
}
int find(int x)
{
if (x == parent[x])
{
return x;
}
find(parent[x]);
}
int count()
{
int res = 0;
for (int i = 0; i < parent.size(); ++i)
{
res += (parent[i] == i);
}
return res;
}
};
利用路径压缩优化 find 方法
基于循环的路径压缩优化
相当于对路径进行了折叠,每个父节点都直接指向了父节点的父节点,如下图所示:
int find(int x)
{
while(x != parent[x])
{
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
基于递归的路径压缩优化
由于是深度优先搜索,所以每次 find 相当于把所有的节点都直接指向了根节点,从父节点开始,每个节点都指向了根节点,如下图所示:
int find(int x)
{
if (x == parent[x]) // 只有根节点满足条件
{
return x;
} else {
parent[x] = find(parent[x]);
return parent[x];
}
// --- or ---
if (x != parent[x])
{
parent[x] = find(parent[x]);
}
return parent[x]; // 这里必须要返回 parent[x]
}
利用 rank 优化 unite 方法
- rank 表示树(集合)的深度
我们在合并集合的时候,有时候随机的合并可能会造成合并成了上面图的类似链表的样子,因为我们没有考虑到两个树(集合)的深度,一味的向一个树(集合)合并,那么就会导致find 方法的复杂度变高,而一个比较明智的选择是我利用rank来记录每个树(集合)的高度,利用如下策略来合并:
- 如果 集合A 的秩 > 集合B 的秩,那么由 A 牵头合并
- 如果 集合B 的秩 > 集合A 的秩,那么由 B 牵头合并
- 如果两个集合的秩相等,那么由 B牵头合并,然后 rank[B]++
可以在纸上画一画,感受一下为什么要这么做。
void unite(int x, int y)
{
int px = find(x);
int py = find(y);
if (px != py)
{
if (rank[px] > rank[py])
{
parent[py] = px;
} else if (rank[py] > rank[px]){
parent[px] = py;
} else {
parent[px] = py;
rank[py]++;
}
}
}
复杂度
-
性能跟树的深度有关系,简称 O(h)
-
并查集并不是一个二叉树,而是一个多叉树,所以并查集的查询和合并时间复杂度并不是O(log n)
-
在加上rank和路径压缩优化后 ,并查集的时间复杂度为 O(log n)
参考
https://blog.csdn.net/weixin_40374341/article/details/93979529
https://blog.csdn.net/weixin_40374341/article/details/93979529
https://blog.csdn.net/yuzhiqiang666/article/details/80721436