板子:并查集(教材口径 + 路径压缩)
用双亲数组
S[]表示森林:**S[x] >= 0表示x的双亲;S[x] < 0表示x是根,绝对值是这个集合的元素个数。**合并时小树并到大树,查找时顺便压缩路径。
代码
void Initial(int S[], int n) { // n 个元素,各自成一个集合
for (int i = 0; i < n; i++) S[i] = -1; // 根的 S 为负数:-1 表示集合里只有 1 个元素
}
int Find(int S[], int x) { // 返回 x 所在集合的根,同时压缩路径
int root = x;
while (S[root] >= 0) root = S[root]; // 第一趟:往上走,找到根(S 为负数的那个)
while (x != root) { // 第二趟:把路径上的每个结点直接挂到根下
int t = S[x]; // 先记下原来的双亲
S[x] = root;
x = t;
}
return root;
}
void Union(int S[], int Root1, int Root2) { // 小树并到大树;两个参数都必须是根
if (Root1 == Root2) return; // 同一个集合,不用合并
if (S[Root2] > S[Root1]) { // S 是负数:S 越大,元素越少,所以 Root2 是小树
S[Root1] += S[Root2]; // 先把元素个数加到大树的根上
S[Root2] = Root1; // 再把小树的根挂过去
} else { // Root1 是小树(一样大时也走这里)
S[Root2] += S[Root1];
S[Root1] = Root2;
}
}
// 合并 a、b 所在的集合:Union(S, Find(S, a), Find(S, b));复杂度:同时使用按大小合并和路径压缩后,每次操作的均摊时间接近
关键边界
| 位置 | 为什么 |
|---|---|
S[Root2] > S[Root1] | 比较的是两个负数。S 更大的那棵树元素更少 |
先 += 再赋值 | 如果先执行 S[Root2] = Root1,S[Root2] 里的元素个数就被覆盖了,再加就会出错 |
Union 的参数必须是根 | 不是根的话,S[] 里存的是双亲的下标,不是元素个数。所以要先 Find |
| 一样大时 Root1 挂到 Root2 下 | 这是教材代码的写法。选择题让画合并后的数组时,按这个规则来 |
算竞写法(认得就行)
// 速记:算竞写法,考场上不用
int fa[MaxSize]; // 初始化:fa[i] = i,根满足 fa[x] == x
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }考场上按教材口径写。题目给出一个数组让你画森林时,负数的绝对值是集合的元素个数,不是树高。口径差异的完整对照见 5.5 哈夫曼树与并查集。
链接
- 📕 返回:数据结构代码板子
- ⬅️ 上一篇:用前序 + 中序建树
- ➡️ 下一篇:中序线索化
- 🔗 5.5 哈夫曼树与并查集