当前位置:   article > 正文

数据结构——并查集

数据结构——并查集

1. 什么是并查集

计算机科学中,并查集(英文:Disjoint-set data structure,直译为不数据结构交集)是一种数据结构,用于处理一些不交集(Disjoint sets,一系列没有重复元素的集合)的合并及查询问题。

举个简单的例子

一个组织里来个10个新人,它们的编号为从1~10,假设此时它们根据彼此的老家而互相亲近(即形成一个小团体),我们要将这10个人划分成不同的小团体,这样划分后形成的结果就是并查集。

接下来我们就来模拟这10个人的划分过程,首先我们可以将每个人的初始值设置为-1,当两个人都属于同一个小团体时,将其中一个人的-1加到另一个人身上,自己的值指向另一个人的下标, 此时另一个人就作为这一个小团体的,这之后如果有新的人进入这个团体,将新人的-1加到根上,再让新人指向根的下标。这样操作后,所有人中只要自己的值<0,则表明自己是一个团队的根,绝对值表示团队人数,而团体中的其他人均指向这个根。

2. 并查集的常见操作

我们通过上面的模拟,我们可以发现并查集的主要操作主要是:合并查找根查看团队个数

我们使用C++实现这个数据结构有

  1. #pragma once
  2. #include <vector>
  3. using namespace std;
  4. class UnionFindSet
  5. {
  6. public:
  7. // 将并查集中的所有元素初始化为-1
  8. UnionFindSet(size_t n)
  9. :_ufs(n, -1)
  10. {}
  11. // 查找根
  12. int FindRoot(int x);
  13. // 合并
  14. void Union(int x1, int x2);
  15. // 查看集合个数
  16. size_t UnionCount(int x);
  17. private:
  18. vector<int> _ufs;
  19. };

1. 查找根

  1. // 查找根
  2. int FindRoot(int x)
  3. {
  4. if (x >= _ufs.size())
  5. {
  6. throw invalid_argument("无效参数!");
  7. return -1;
  8. }
  9. int root = x;
  10. while (_ufs[root] >= 0) // 不为根就向上查找
  11. {
  12. root = _ufs[root];
  13. }
  14. return root;
  15. }

2. 合并

在合并时,有可能会出现两个团队互相合并的情况,此时我们只需要将其中一个团队的根的值加到另一个团队的根上,再让自己指向另一个团队的根即可,即

  1. // 合并
  2. void Union(int x1, int x2)
  3. {
  4. int root1 = FindRoot(x1);
  5. int root2 = FindRoot(x2);
  6. // 两个人属于不同的集合时,才需要进行合并
  7. if (root1 != root2)
  8. {
  9. _ufs[root1] += _ufs[root2];
  10. _ufs[root2] = root1;
  11. }
  12. }

3. 查看集合个数

  1. // 查看集合个数
  2. size_t UnionCount()
  3. {
  4. size_t ret = 0;
  5. for (auto& e : _ufs)
  6. {
  7. if (e < 0) ret++;
  8. }
  9. return ret;
  10. }

3. 并查集的实际应用

1. 省份数量

题目链接:LCR 116. 省份数量 - 力扣(LeetCode)

解析:分析题目,如果这道题使用并查集就没有那么难,整体思路就是如果两个城市相连就将他们合并为一个省份,最终返回省份个数即可

解法一:使用并查集数据结构

  1. class UnionFindSet
  2. {
  3. public:
  4. // 将并查集中的所有元素初始化为-1
  5. UnionFindSet(size_t n)
  6. :_ufs(n, -1)
  7. {}
  8. // 查找根
  9. int FindRoot(int x)
  10. {
  11. if (x >= _ufs.size())
  12. {
  13. throw invalid_argument("无效参数!");
  14. return -1;
  15. }
  16. int root = x;
  17. while (_ufs[root] >= 0) // 不为根就向上查找
  18. {
  19. root = _ufs[root];
  20. }
  21. return root;
  22. }
  23. // 合并
  24. void Union(int x1, int x2)
  25. {
  26. int root1 = FindRoot(x1);
  27. int root2 = FindRoot(x2);
  28. // 两个人属于不同的集合时,才需要进行合并
  29. if (root1 != root2)
  30. {
  31. _ufs[root1] += _ufs[root2];
  32. _ufs[root2] = root1;
  33. }
  34. }
  35. // 查看集合个数
  36. size_t UnionCount()
  37. {
  38. size_t ret = 0;
  39. for (auto& e : _ufs)
  40. {
  41. if (e < 0) ret++;
  42. }
  43. return ret;
  44. }
  45. private:
  46. vector<int> _ufs;
  47. };
  48. class Solution
  49. {
  50. public:
  51. int findCircleNum(vector<vector<int>>& isConnected)
  52. {
  53. UnionFindSet ufs(isConnected.size());
  54. for (int i = 0; i < isConnected.size(); i++)
  55. for (int j = 0; j < isConnected[i].size(); j++)
  56. if (isConnected[i][j] == 1)
  57. {
  58. ufs.Union(i, j);
  59. }
  60. return ufs.UnionCount();
  61. }
  62. };

 解法二:直接运用并查集思想

  1. class Solution
  2. {
  3. public:
  4. int findCircleNum(vector<vector<int>>& isConnected)
  5. {
  6. vector<int> ufs(isConnected.size(), -1);
  7. auto FindRoot = [&ufs](int x)
  8. {
  9. while (ufs[x] >= 0) x = ufs[x];
  10. return x;
  11. };
  12. for (int i = 0; i < isConnected.size(); i++)
  13. for (int j = 0; j < isConnected[i].size(); j++)
  14. if (isConnected[i][j] == 1)
  15. {
  16. int root1 = FindRoot(i);
  17. int root2 = FindRoot(j);
  18. if (root1 != root2)
  19. {
  20. ufs[root1] += ufs[root2];
  21. ufs[root2] = root1;
  22. }
  23. }
  24. int ret = 0;
  25. for (auto& e : ufs)
  26. if (e < 0) ret++;
  27. return ret;
  28. }
  29. };

2. 等式方程的可满足性

题目链接:990. 等式方程的可满足性 - 力扣(LeetCode)

解析:分析题目,我们可以使用并查集来解决这道题,即先将所有等式挑选出来,将它们中的元素合并,这之后再查看不等式,如果方程两边的元素同属于一个集合则证明相悖,解决如下

  1. class Solution
  2. {
  3. public:
  4. bool equationsPossible(vector<string>& equations)
  5. {
  6. vector<int> ufs(26, -1);
  7. auto FindRoot = [&ufs](int x)
  8. {
  9. while (ufs[x] >= 0) x = ufs[x];
  10. return x;
  11. };
  12. for (auto& s : equations)
  13. {
  14. if (s[1] == '=')
  15. {
  16. int root1 = FindRoot(s[0] - 'a');
  17. int root2 = FindRoot(s[3] - 'a');
  18. if (root1 != root2)
  19. {
  20. ufs[root1] += ufs[root2];
  21. ufs[root2] = root1;
  22. }
  23. }
  24. }
  25. for (auto& s : equations)
  26. {
  27. if (s[1] == '!')
  28. {
  29. int root1 = FindRoot(s[0] - 'a');
  30. int root2 = FindRoot(s[3] - 'a');
  31. if (root1 == root2) return false;
  32. }
  33. }
  34. return true;
  35. }
  36. };

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/777934
推荐阅读
相关标签
  

闽ICP备14008679号