文章目录
- 算法思想
- 代码模板
- 题目描述:
- 代码
- 并查集模板
- 模板题二(求并查集内集合的数量)
算法思想
并查集的核心操作:
- 将两个集合合并
- 询问两个元素是否在一个集合中
基本原理:每个集合我们将他维护成一颗树,根节点的值就作为集合的编号,每个节点存储他的父节点,p[x] 就是 x 的父节点
- 当 p[x] == x 就证明 p[x] 是树根,就证明 x 指向的是根节点
- 我们可以用 while (p[x] != x) x = p[x] 来找到 x 的集合编号
- 我们可以用集合 A 的根节点连接上集合 B 的根节点的方式合并两个集合
接下来,看模板
代码模板
模板题:AcWing 836. 合并集合
题目描述:
代码
#include <iostream>
#include <vector>using namespace std;const int N = 100010;vector<int> p(N);// 求 x 的祖先节点(集合的编号)
int find(int x) {if (p[x] != x) p[x] = find(p[x]); // 如果 p[x] 不是 x 的祖先节点, 求 p[x] 的祖先并赋值给 p[x]return p[x]; // 返回 p[x]
}int main()
{int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) p[i] = i;while (m -- ) {char ch;int a, b;cin >> ch >> a >> b;if (ch == 'M') p[find(b)] = find(a);else {if (find(a) == find(b)) cout << "Yes" << endl;else cout << "No" << endl;}}return 0;
}
并查集模板
// 求 x 的祖先节点(集合的编号)+ 路径压缩
int find(int x) {// 如果 p[x] 不是 x 的祖先节点, 求 p[x] 的祖先并赋值给 p[x]if (p[x] != x) p[x] = find(p[x]); return p[x]; // 返回 p[x]
}
并查集的核心操作 find 求 x 集合的编号(也就是求 x 的祖先节点),这模板包含并查集的路径压缩优化
如何理解?
传进来的参数 x 是集合内的一个数,我们调用 find(x) 就是为了求这个数的集合编号是什么(或者说,求 x 的祖先节点的编号)
记住前面并查集的性质,p[x] 是 x 的父节点,而 p[x] != x,而并查集的根节点的值就是并查集的编号,所以当 p[x] == x 的时候就证明 p[x] 是 x 的祖宗节点,所以这里 if (p[x] != x) p[x] = find(p[x]) 的操作
实际上就是:当 p[x] 不是 x 的祖宗节点,就让 p[x] = find(p[x]) 找他的祖宗节点,找到之后返回 p[x] 就是我们要求的 x 的祖宗节点,也就是集合的编号了
纯享版
int find(int x) {if (p[x] != x) p[x] = find(p[x]); return p[x];
}
模板题二(求并查集内集合的数量)
题目链接:AcWing 837. 连通块中点的数量
#include <iostream>
#include <vector>
#include <string>using namespace std;const int N = 100010;vector<int> p(N), Size(N);int find(int x) {if (p[x] != x) p[x] = find(p[x]);return p[x];
}int main()
{int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) {p[i] = i;Size[i] = 1;}while (m -- ) {string s;int a, b;cin >> s;if (s == "C") {cin >> a >> b;if (find(a) == find(b)) continue; // 如果 a b 在同一个集合里, 那就没必要操作了Size[find(b)] += Size[find(a)]; // 将集合 a 元素的数量更新到集合 b 中p[find(a)] = find(b); // 合并集合 a, b(集合 a 并入集合 b)}else if (s == "Q1") {cin >> a >> b;if (find(a) == find(b)) cout << "Yes" << endl;else cout << "No" << endl;}else {cin >> a;cout << Size[find(a)] << endl;}}return 0;
}
我们可以维护一个 Size 数组,根节点上存好集合元素的数量,然后在合并集合的时候维护 Size 数组即可