原理
将图中所有相连的点都指向同一个点,代表这些点都属于同一个集合
实现
需要一个fa[]
数组来记录每个点所指向的点,所有点初始都指向自己(此处可以理解为fa[]
用来指向自己的父亲,初始为自己指向自己,虽然我就是我自己的父亲听起来有点怪怪的)
初始化
for(int i = 0; i <= n; i++){ fa[i] = i; }
并查集(模板)
int Find(int x){
return fa[x] = fa[x] == x ? x : Find(fa[x]);
//如果自己指向自己,说明此时已经为该集合的祖先,则返回自己
//如果自己不指向自己,说明自己不是该集合的祖先,则继续寻找该集合的祖先,并将自己的父节点变为变为该集合的祖先
}
void Union(int x, int y){
fa[fd(x)] = fd(y);
//将x的祖先更新为y的祖先
}
上面这两个函数就是并查集的模板(确实只有两句话)
注意事项
我们每次查找两个点是否属于同一个集合的时候,一定要用下面的方法:
int x, y;
//判断x点和y点是否属于同一祖先
fd(x) == fd(y)
下面这个是错误示例:
//错误示例
fa[x] == fa[y]
//错误示例
为什么会有这个错误呢?这里举个例子,顺序合并点
1 2
1 3
3 4
此时我们可以发现,当只链接3和4的时候,因为3已经是一个集合的祖先了,但是它的孩儿们依旧指向他们的祖先,即点3,此时有
fa[1] = 3;
fa[2] = 3;
fa[3] = 4;
fa[4] = 4;
所以此时如果用了错误示例来查找1
和3
是否是同一个祖先的时候会发现fa[1] == 3
但是fa[3] == 4
,因此会判断不在同一个集合中,但是如果是fa[Find(1)]
进行查找的时候,则会对点1
的祖先进行更新,此时有fa[1] == 4
.