用途
并查集可以用来维护一类具有传递性的关系
实现
查询祖先节点
对于普通并查集来说
int Find (int x) {
if (x != fa[x]) fa[x] = Find(fa[x]);
return fa[x];
}
按秩合并则取点数小的连向点数大的即可
而对于带边权的并查集
int Find (int x) {
if (x != fa[x]) {
int mark = fa[x];
fa[x] = Find(fa[x]), val[x] += val[mark];
}
return fa[x];
}
由于路径压缩后 $x$ 的父亲就是根节点了
另外Find(x)
合并两个结点所在集合
对于普通并查集来说
而对于带权并查集而言
bool Merge (int x, int y, int w) {
int Rx = Find(x), Ry = Find(y);
if (Rx != Ry) fa[Rx] = Ry, val[Rx] = val[y] + w - val[x];
return val[Rx] + val[x] - val[y] == w;
}
Code
带路径压缩和按秩合并优化的普通并查集 :
int Find (int x) {
if (x != fa[x]) fa[x] = Find(fa[x]);
return fa[x];
}
bool Merge (int x, int y) {
int Rx = Find(x), Ry = Find(y);
if (x != y) {
if (pnum[Rx] < pnum[Ry])
fa[Rx] = Ry, pnum[Ry] += pnum[Rx];
else fa[Ry] = Rx, pnum[Rx] += pnum[Ry];
}
return Rx != Ry;
}
带路径压缩和按秩合并优化的带权并查集
int Find (int x) {
if (x != fa[x]) {
int mark = fa[x];
fa[x] = Find(fa[x]), val[x] += val[mark];
}
return fa[x];
}
bool Merge (int x, int y, int w) {
int Rx = Find(x), Ry = Find(y);
if (Rx == Ry) return (val[x] - val[y] == w);
if (pnum[Rx] < pnum[Ry])
fa[Rx] = Ry, pnum[Ry] += pnum[Rx], val[Rx] = val[y] + w - val[x];
else fa[Ry] = Rx, pnum[Rx] += pnum[Ry], val[Ry] = val[x] - w - val[y];
return true;
}
不带任何优化的并查集复杂度是 $\Theta(n ^ 2)$ 的
带路径压缩的并查集时间复杂度 $\Theta(n \log n)$
再加上按秩合并可以做到严格 $\Theta(n \alpha(n))$