• 【数据结构与算法】并查集


    目录

    一、并查集原理

    二、并查集实现

    三、并查集应用

    547. 省份数量 - 力扣(LeetCode)

    总结


    一、并查集原理

    并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:

    • 合并(Union):把两个不相交的集合合并为一个集合。
    • 查询(Find):查询两个元素是否在同一个集合中。

    在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询某一个元素归属于那个集合的运算。适合于描述这类问题的抽象数据类型称为并查集(union-fifind-set)。

    通俗的来说就是找朋友问题

    假如现在有10个人,分别是{0,1,2,3,4,5,6,7,8,9}

    他们有些人之间是朋友,现在将他们按照朋友圈划分为几个小集合

    有一天7和4成为了好朋友,那么就需要将左侧两个朋友圈合并成为一个朋友圈

    这就是并查集要解决的问题

    并查集实际上是一个森林,它是由多棵树构成的

    我们这里采用的表示树的方法与之前的二叉树不同,类似于堆,利用数组下标来确定父子关系

    初始条件,每一个位置都是-1,代表每一个人都是一个小集合

    然后合并朋友圈

    仔细观察数组中存的值,可以得出以下结论:
    1. 数组的下标对应集合中元素的编号
    2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
    3. 数组中如果为非负数,代表该元素双亲在数组中的下标

    二、并查集实现

    并查集的主要接口就只有两个Union和FindRoot

    首先来说FindRoot

    寻找根节点,什么属于根节点呢?

    数组中存储的是负数的下标就是根的位置,这句话有一点绕

    我们要找根节点,就是找数组不为负数的位置

    1. size_t FindRoot(int x)
    2. {
    3. if(x < 0 || x >= _set.size())
    4. {
    5. throw invalid_argument("非法参数");
    6. return -1;
    7. }
    8. size_t parent = x;
    9. while(_set[parent] >= 0)
    10. {
    11. parent = _set[parent];
    12. }
    13. return parent;
    14. }

    有了FindRoot的基础之后Union,就容易理解了

    Union是将两个集合合并,我们只要两个参数就可以x1,x2

    我们先分别查找x1和x2的父亲,如果他们是同一个父亲,就不需要合并,如果他们的父亲不同,就将他们合并。

    合并也十分简单

    我们直接将x2父亲加到x1父亲上,并且将x2的父亲更改为x1的父亲

    1. void Union(int x1, int x2)
    2. {
    3. size_t root1 = FindRoot(x1);
    4. size_t root2 = FindRoot(x2);
    5. _set[root1] += _set[root2];
    6. _set[root2] = root1;
    7. }

    最后一个接口是SetCount,是用来计算并查集现在有几个集合的,也就是有几棵树

    很简单,遇到数组中的负数,计数器就自增

    1. size_t SetSize()
    2. {
    3. size_t size = 0;
    4. for(const auto& e : _set)
    5. {
    6. if(e < 0)
    7. {
    8. size++;
    9. }
    10. }
    11. return size;
    12. }

    完整代码:

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. class UnionFindSet
    7. {
    8. public:
    9. UnionFindSet(size_t n)
    10. :_set(n, -1)
    11. {}
    12. size_t FindRoot(int x)
    13. {
    14. if(x < 0 || x >= _set.size())
    15. {
    16. throw invalid_argument("非法参数");
    17. return -1;
    18. }
    19. size_t parent = x;
    20. while(_set[parent] >= 0)
    21. {
    22. parent = _set[parent];
    23. }
    24. return parent;
    25. }
    26. void Union(int x1, int x2)
    27. {
    28. size_t root1 = FindRoot(x1);
    29. size_t root2 = FindRoot(x2);
    30. _set[root1] += _set[root2];
    31. _set[root2] = root1;
    32. }
    33. size_t SetSize()
    34. {
    35. size_t size = 0;
    36. for(const auto& e : _set)
    37. {
    38. if(e < 0)
    39. {
    40. size++;
    41. }
    42. }
    43. return size;
    44. }
    45. private:
    46. vector<int> _set;
    47. };

    三、并查集应用

    547. 省份数量 - 力扣(LeetCode)

    这道题是典型的并查集应用,用来判断到底最后被划分为几个集合

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


    总结

    以上就是今天要讲的内容,本文仅仅讲解了并查集的简单实现及应用

  • 相关阅读:
    JVM-垃圾回收
    node.js 使用 express-jwt 报错:expressJWT is not a function
    C++笔记2(内存分区模型,引用)
    内核态和用户态
    戳印序列原理及C++实现方法一
    javaEE--后端环境变量配置
    基于AlexNet卷积神经网络的手写体数字识别系统研究-附Matlab代码
    13 - 多线程之锁优化(中):深入了解Lock同步锁的优化方法
    @weakify 与 @strongify 实现原理
    某物联网数智化园区行业基于 KubeSphere 的云原生实践
  • 原文地址:https://blog.csdn.net/m0_62179366/article/details/128192431