并查集是一种数据结构,可以快速合并多个集合,查询元素是否在相同集合中,维护”朋友的朋友是朋友“这样一种关系
操作一,初始化并查集,就是将各个节点的父亲节点初始化为自己:
for (int i = 1; i <= n; i ++) {
father[i] = i;
}
操作二,给出元素x,询问这个元素所在树的根节点:
int root(int x) {
if(x == father[x]) {
return x;
}
else {
return root(father[x]); //递归查找父亲节点的父亲
}
}
操作三,给两个元素,将两个元素所在集合合并:
即把一个树的树根连接在另一棵树的树根上,前提要保证这两个集合互不相关~
int rx = root(x), ry = root(y); //找到x和y的根节点
if(rx != ry) { //保证两个树的树根不相同,表示不是同一个集合
father[rx] = ry; //其中一个根认另一个根做父亲,两个树有了一个共同的父亲节点,两个集合就变成一个集合
}
上述查,并一次时间复杂度均为O(1)
并查集的优化:
一、路径压缩:将所有元素直接连到根节点上
int root(int x) {
if(x == father[x]) {
return x; //这个元素已经直接连接到根节点上了,返回根的值
}
else {
return father[x] = root(father[x]); //这个元素的父亲节点不是根,递归查找这个元素父节点的父节点,直到找到根节点,建立连接
}
}
二、按秩合并:合并过程中避免长链。
保存每个树的高度,在合并过程中,让较高树的祖先作为新的祖先,尽量防止树的高度变大。
以上两种优化方法,任一种都可以让单次操作 均摊时间复杂度变成O(n * log(n))
同时使用两种优化,单次操作均摊时间复杂度可以降至O(α(n)),α为反阿克曼函数。
并查集的分类:
二分图染色,
拆点并查集,
种类并查集(又叫加权并查集,有异或运算,是一种重要的并查集,维护”敌人的敌人是朋友“这样一种重要关系)
种类并查集查找根节点模板:
int find(int x) {
if(x == p[x]) {
return x;
}
int t = p[x];
p[x] = find(p[x]);
d[x] = d[t] ^ d[x];
return p[x];
}
种类并查集合并集合,模板:
int pa = find(a), int pb = find(b);
if(pa != pb) {
p[pb] = pa;
d[pb] = d[a] ^ d[b] ^ 1;
}