当前位置: 首页 > article >正文

【高阶数据结构】并查集

> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:了解什么是并查集,并能简单的模拟实现。

> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!

> 专栏选自:数据结构

> 望小伙伴们点赞👍收藏✨加关注哟💕💕

一、并查集的原理

概念:

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

举个栗子:

某旅游团内有游客10人,其中西安4人,成都3人,武汉3人,10个人来自不同的地方,起先互不相识,每个游客都是一个独立的小团体,现给这些游客进行编号:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 给以下数组用来存储该小集体,数组中的数字代表:该小集团中具有成员的个数(负数的意义下文讲解)

旅行结束后,游客们要乘车回家,每个地方的游客自发组织成小分队一起上路,于是:西安游客小分队s1={0,6,7,8},成都游客小分队s2={1,4,9},武汉游客小分队s3={2,3,5}就相互认识了,10个人形成了三个小团体。假设右三个群主0,1,2担任队长,负责大家的出行。

一趟火车之旅后,每个小分队成员就互相熟悉,称为了一个朋友圈。

总结:

  • 数组的下标对应集合中元素的编号。
  • 数组中如果为负数,负号代表根,数字代表该集合中元素个数。
  • 数组中如果为非负数,代表该元素双亲在数组中的下标。

在公司工作一段时间后,西安小分队中8号同学与成都小分队1号同学奇迹般的走到了一起,两个
小圈子的学生相互介绍,最后成为了一个小圈子:

通过以上例子可知,并查集一般可以解决一下问题:

  • 查找元素属于哪个集合

沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)。

  • 查看两个元素是否属于同一个集合

沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在。

  • 将两个集合归并成一个集合
  • 将两个集合中的元素合并。
  • 将一个集合名称改成另一个集合的名称。
  • 集合的个数 

遍历数组,数组中元素为负数的个数即为集合的个数。

二、并查集的实现

并查集功能说明:

  • 查找元素属于哪个集合。
  • 查看两个元素是否属于同一个集合。
  • 将两个集合归并成一个集合。
  • 集合的个数。

代码实现:

#include <iostream>
#include <vector>
 
class UnionFind {
private:
    std::vector<int> parent;
    std::vector<int> rank;
 
public:
    UnionFind(int n) : parent(n), rank(n) {
        for (int i = 0; i < n; ++i) {
            parent[i] = i;
            rank[i] = 1;
        }
    }
 
    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }
 
    void unite(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) return;
 
        if (rank[rootX] > rank[rootY]) {
            parent[rootY] = rootX;
        } else if (rank[rootX] < rank[rootY]) {
            parent[rootX] = rootY;
        } else {
            parent[rootY] = rootX;
            rank[rootX] += 1;
        }
    }
 
    bool connected(int x, int y) {
        return find(x) == find(y);
    }
};
 
int main() {
    int n, m;
    std::cin >> n >> m;
    UnionFind uf(n);
    for (int i = 0; i < m; ++i) {
        int a, b;
        std::cin >> a >> b;
        uf.unite(a - 1, b - 1); // 转换为0-based索引
    }
    for (int i = 0; i < m; ++i) {
        int a, b;
        std::cin >> a >> b;
        std::cout << (uf.connected(a - 1, b - 1) ? "YES" : "NO") << std::endl;
    }
    return 0;
}

三、并查集的应用

省份数量

class Solution {
public:
    size_t FindRoot(int x)
    {
        while(ufs[x] >= 0)
        {
            x = ufs[x];
        }
        return x;
    }
    int findCircleNum(vector<vector<int>>& isConnected) {
        ufs.resize(isConnected.size(), -1);
        for(int i = 0; i < isConnected.size(); i++)
        {
            for(int j = 0; j < isConnected[i].size(); j++)
            {
                if(isConnected[i][j] == 1)
                {
                    int root1 = FindRoot(i);
                    int root2 = FindRoot(j);
                    if(root1 != root2)
                    {
                        if(abs(ufs[root1]) < abs(ufs[root2]))
                            swap(root1, root2);
                        ufs[root1] += ufs[root2];
                        ufs[root2] = root1;
                    }
                }
            }
        }
        int cnt = 0;
        for(auto& e : ufs)
            if(e < 0) cnt++;
        return cnt;            
    }
private:
    vector<int> ufs;
};

等式方程的可满足性

class Solution {
public:
    int Find(int x)
    {
        while(ufs[x] >= 0)
            x = ufs[x];
        return x;
    }
    bool equationsPossible(vector<string>& equations) {
        ufs.resize(26, -1);
        for(auto& str : equations)
        {
            if(str[1] == '=')
            {
                int root1 = Find(str[0]-'a');
                int root2 = Find(str[3]-'a');
                if(root1 != root2)
                {
                    if(abs(ufs[root1]) < abs(ufs[root2]))
                        swap(root1, root2);
                    ufs[root1] += ufs[root2];
                    ufs[root2] = root1;
                }
            }
        }
        for(auto& str : equations)
        {
            if(str[1] == '!')
            {
                int root1 = Find(str[0]-'a');
                int root2 = Find(str[3]-'a');
                if(root1 == root2)
                    return false;
            }
        }
        return true;
    }
private:
    vector<int> ufs;
};

四、结束语 

今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。

​​ 


http://www.kler.cn/a/406736.html

相关文章:

  • Rocky Linux 系统安装/部署 Docker
  • Python中Tushare(金融数据库)入门详解
  • 人工智能(AI)与机器学习(ML)基础知识
  • 深入理解TensorFlow中的形状处理函数
  • DB-GPT V0.6.2 版本更新:牵手libro社区、GraphRAG图谱构建能力增强等
  • Shell编程-8
  • RPC学习
  • 安宝特分享 | 如何利用AR技术革新医疗实践:从远程急救到多学科协作
  • QT QChart+Eigen库绘制线性回归散点图
  • 【电路笔记】-布尔逻辑AND函数
  • uniapp接入BMapGL百度地图
  • 使用 cnpm 安装 Electron,才是正确快速的方法
  • 蓝桥杯每日真题 - 第21天
  • Java根据前端返回的字段名进行查询数据的方法
  • 淘宝评论大冒险:Java爬虫的“探险记”
  • react native 安装好apk后无法打开
  • Vue3 el-table 默认选中 传入的数组
  • 深度学习1
  • 数据结构之树与二叉树
  • C语言:空指针详细解读
  • 实用功能,觊觎(Edge)浏览器的内置截(长)图功能
  • 《鸿蒙系统:开启智能新时代的璀璨之星》
  • MySQL中的CAST类型转换函数
  • docker 部署 kvm 图形化管理工具 WebVirtMgr
  • 论文翻译 | RECITATION-AUGMENTED LANGUAGE MODELS
  • Spark 之 Aggregate