并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的合并查询问题。 它支持两种操作:

  • 查找(Find):确定某个元素处于哪个子集;
  • 合并(Union):将两个子集合并成一个集合。

一个功能完全的类模板(参考力扣):

并查集类模板

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. class UnionFind {
  4. public:
  5. int n; // 初始节点数
  6. int setN; // 集合数量
  7. vector<int> parent; // 祖宗节点
  8. vector<int> size; // 合并优化操作使用,记录集合大小
  9. // 通过构造函数初始化数组
  10. UnionFind(int _n): n(_n), setN(_n), parent(_n), size(_n, 1) {
  11. for (int i=0; i<_n; ++i) parent[i] = i;
  12. }
  13. int find(int x) {
  14. return parent[x] == x ? x : parent[x] = find(parent[x]);
  15. }
  16. void unite(int x, int y) {
  17. x = find(x);
  18. y = find(y);
  19. if (x == y) {
  20. return;
  21. }
  22. if (size[x] > size[y]) {
  23. swap(x, y);
  24. }
  25. parent[x] = y;
  26. size[y] += size[x];
  27. --setN;
  28. }
  29. bool isCon(int x, int y) {
  30. return find(x) == find(y);
  31. }
  32. };
  33. int n, m;
  34. int main() {
  35. cin >> n >> m;
  36. UnionFind* uf = new UnionFind(n);
  37. // 合并两个元素所在集合
  38. cin >> x >> y;
  39. uf->unite(x, y);
  40. // 询问两个元素是否在一个集合中
  41. cin >> x >> y;
  42. cout << (uf->isCon(x, y) ? "Yes" : "No") << endl;
  43. return 0;
  44. }

例题 1:蓝桥 合根植物【第八届】【决赛】

这是一道模板题,你可以在读懂模板的基础上,先行自己尝试。

参考代码
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. class UnionFind {
  4. public:
  5. int n;
  6. int setN;
  7. vector<int> parent;
  8. vector<int> size;
  9. UnionFind(int _n): n(_n), setN(_n), parent(_n), size(_n, 1) {
  10. for (int i=0; i<_n; ++i) parent[i] = i;
  11. }
  12. int find(int x) {
  13. return parent[x] == x ? x : parent[x] = find(parent[x]);
  14. }
  15. void unite(int x, int y) {
  16. x = find(x);
  17. y = find(y);
  18. if (x == y) {
  19. return;
  20. }
  21. if (size[x] > size[y]) {
  22. swap(x, y);
  23. }
  24. parent[x] = y;
  25. size[y] += size[x];
  26. --setN;
  27. }
  28. bool isCon(int x, int y) {
  29. return find(x) == find(y);
  30. }
  31. };
  32. int n, m;
  33. int k;
  34. int main() {
  35. cin >> n >> m >> k;
  36. UnionFind* uf = new UnionFind(n * m);
  37. while (k--) {
  38. int x, y;
  39. cin >> x >> y;
  40. uf->unite(x - 1, y - 1); // 本题序号从 1 起
  41. }
  42. cout << uf->setN << endl;
  43. return 0;
  44. }

并查集由于属于一种树形结构,通常不需要做所有的优化,它的复杂度解释起来比较复杂,你可以理解为每次操作接近5.11.1 并查集 - 图1。熟练之后,你可以不必默写整个模板,使用数组模拟,有简便写法:

  1. int p[N]; //存储每个点的祖宗节点
  2. // 返回x的祖宗节点
  3. int find(int x) {
  4. return p[x] == x ? x : p[x] = find(p[x]);
  5. }
  6. // 初始化,假定节点编号是1~n
  7. for (int i=1; i<=n; ++i) {
  8. p[i] = i;
  9. }
  10. // 合并a和b所在的两个集合:
  11. p[find(a)] = find(b);

这样一来,你可以灵活调整题目给定编号从 1 起的状况,减少代码量。你可以自己想一想怎么实现模板的查询当前不连通集合数量 setN 的操作。

推荐阅读:算法学习笔记(1) : 并查集