前言
笔试中碰到了类似题目,一下子就反应过来要用并查集,但是忘了如何构造并查集了,记录下来随时复习。
概念
主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
- 合并(Union):把两个不相交的集合合并为一个集合。
- 查询(Find):查询两个元素是否在同一个集合中。
举个例子:有N个人,每个人编号为1、2……N。给出两两亲戚关系,如[1, 3]代表一号和三号是亲戚,给出一组这样的亲戚关系,问共有多少个家族(家族中的人有亲戚关系)?
并查集
可以用一个人来代表一个家族,这个人可以是任意的。
初始化
先假设所有人都没有亲戚关系,所以每个人都代表一个家族。
int uf[MAXN];
void init(int n) {
for (int i = 1; i <= n; ++i) {
uf[i] = i;
}
}
查询
递归的去寻找家族的代表人。因此判断两个人是否是同一个家族,只需要判断代表人是否是同一个人。
int find(int i) {
if(uf[i] == i) {
return i;
} else {
return find(uf[i]);
}
}
合并
找到i和j家族的代表人,然后将i家族的代表人替换为j家族的代表人即可。
void merge(int i, int j) {
uf[find(i)] = find(j);
}
优化
完成以上最简单的并查集的初始化以及查询、合并两个功能,就可以解决大部分并查集问题了。
还有一些优化措施,如路径压缩等,详见参考。