一、适用场景
并查集是一种非常简单的数据结构,适合具有传递性关系的问题,尤其是连通性问题。
二、结构定义
1、并查集的实质是一颗森林,由多棵树组成,每一棵树代表一个集合
2、每个集合的代表元是这棵树的根节点
3、C++定义如下,一般采用静态数组存储:
class UnionFind {
//父节点数组 pa[i] 表示节点 i 的父节点下标
std::vector<int> pa;
//大小数组 sizes[i] 表示以 i 作为根的集合内的元素个数
std::vector<int> sizes;
//连通分量数量,即互不相连的孤岛数量
int c;
};
其中:
①pa:存放节点的父节点的下标
②sizes:存放树的大小
③c:森林中树的个数
三、使用
并查集的核心操作只有两个:查询和合并
1、查询find
//目的:找到节点x所在集合的根
//方法:递归向上寻找,x所在集合的根即其父节点所在集合的根(递归出口:x本身就是一个根)
//代码:
int Find(int x) {
if (pa[x] == x) return x; // 根的父节点是本身
return Find(pa[x]);
}
//路径压缩:找到x的根后可以直接把x嫁接到根上,拉近结点和根的距离,下次找就更快了。这种把查询结果利用起来的方式,就是路径压缩,代码如下:
// 查找一个节点的根节点
int Find(int x) {
if (pa[x] == x) return x;
//这个递归到底后才会再开始执行
pa[x] = Find(pa[x]); // 重设 x 的父节点到根
return pa[x];
}
这样,原本向上递归的路径上的所有节点都会直接嫁接在根下面去
经过路径压缩后,树会变得更加扁平,下次find会更加迅速
2、合并union
//目的:合并两个节点a和b所在的集合
//方法:先用find分别找出两个节点的集合的根,然后把其中一个根的父节点指向另一个根
void Union(int a, int b) {
pa[Find(b)] = Find(a);
}
当然有的时候,合并后的树会变得不平衡,但没关系,只要合并后属于同一个集合就可以了,我们只关心连通性。不过,有一种方法是把更小的树合并到更大的树上去,这样可以一定程度上控制有效性,代码如下:
void Union(int a, int b) {
a = Find(a), b = Find(b);
if (a == b) return; // 已经在同一集合
if (sizes[a] > sizes[b]) { // 把 b 并入 a
pa[b] = a;
sizes[a] += sizes[b];
} else {
pa[a] = b;
sizes[b] += sizes[a];
}
c--; // 连通分量减一
}
四、应用
最经典的模板题就是岛屿题,如:
//把每个岛屿看作一个集合,那么这就是一个求连通分量的问题
//定义并查集中的节点符号
auto f = [=](int i, int j) { return i * n + j; };
f(i,j)的值就是代表方格(i,j)的节点符号,一共有m*n个
//接着,只用顺序扫描整个方格,把1的格子合并到各自归属的集合中去
//由于是自左向右、自上向下扫描,所以只用合并左边和上边相邻的格子的集合
//最后,合并完成后,输出连通分量即可,代码如下:
// 省略 union-find 代码
class Solution {
public:
int numIslands(vector<vector<char>>& g) {
int m = g.size(), n = g[0].size();
//构造并查集
UnionFind uf(m * n);
// 映射到节点标号
auto f = [=](int i, int j) { return i * n + j; };
int k0 = 0; // 0 的方格数量,最后要剔除
for (auto i = 0; i < m; i++) {
for (auto j = 0; j < n; j++) {
if (g[i][j] == '1') {
// 上
if (i > 0 && g[i - 1][j] == '1')
uf.Union(f(i, j), f(i - 1, j));
// 左
if (j > 0 && g[i][j - 1] == '1')
uf.Union(f(i, j), f(i, j - 1));
} else
//这里并查集不合0
//所以每个0都看作一个独立的连通分量
//所以后面直接减去0的个数即可,不用水域相邻问题
k0++;
}
}
return uf.C() - k0; // C() 返回并查集的连通分量
}
};
//注意,最后要减去0的格子的数量
参考: