C++的数据结构(十八):并查集

        并查集(Union-Find)是一种用于处理一些不交集(Disjoint Sets)问题的数据结构。它主要支持两种操作:合并集合(Union)和查找元素所属集合(Find)。在解决诸如连通性问题、网络中的群组问题等场景时,并查集表现出色。

        并查集的基本思想是使用一个数组来表示所有的元素,数组的每个索引对应一个元素,数组的值则表示该元素所属的集合的代表元素(也称为父节点或根节点)。初始时,每个元素都自成一个集合,所以数组的每个值都初始化为其自身的索引。

        查找操作(Find)用于确定两个元素是否属于同一集合,这通常通过递归地查找元素的父节点,直到找到根节点(父节点为自身的节点)为止。在查找过程中,为了加速后续的查找操作,通常还会进行路径压缩,即将查找路径上的所有节点直接指向根节点。

        合并操作(Union)用于将两个集合合并为一个集合。这通常通过将其中一个集合的代表元素设置为另一个集合的代表元素的子节点来实现。在实际应用中,为了保持树的平衡性,常常选择秩(rank)较小的树的根节点作为子节点,秩可以简单理解为树的高度的一个上界。

        下面是一个简单的C++示例,展示了并查集的基本操作,代码如下。

#include <iostream>
#include <vector>
using namespace std; 
class UnionFind {
private:
    vector<int> parent; // 父节点数组
    vector<int> rank;   // 秩数组

public:
    UnionFind(int n) {
        parent.resize(n);
        rank.resize(n, 0);
        for (int i = 0; i < n; ++i) {
            parent[i] = i;
        }
    }

    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) {
            if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
            } else if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
            } else {
                parent[rootY] = rootX;
                rank[rootX]++;
            }
        }
    }

    bool isConnected(int x, int y) {
        return find(x) == find(y);
    }
};

int main() {
    UnionFind uf(5); // 初始化一个包含5个元素的并查集

    uf.unite(0, 1);  // 合并0和1所在的集合
    uf.unite(2, 3);  // 合并2和3所在的集合

    cout << uf.isConnected(0, 1) << endl; // 输出1,表示0和1连通
    cout << uf.isConnected(0, 2) << endl; // 输出0,表示0和2不连通

    uf.unite(1, 3);  // 合并1和3所在的集合,由于0和1连通,2和3连通,所以合并后0-1-3-2都连通

    cout << uf.isConnected(0, 2) << endl; // 输出1,表示0和2连通

    return 0;
}

         在上面的示例中,我们首先创建了一个包含5个元素的并查集。然后,我们合并了0和1所在的集合,以及2和3所在的集合。接着,我们检查0和1是否连通(输出1表示连通),以及0和2是否连通(输出0表示不连通)。最后,我们合并了1和3所在的集合,由于之前0和1连通,2和3连通,所以合并后0-1-3-2都连通,再次检查0和2是否连通时输出1表示连通。

        并查集的应用实例:朋友圈划分。      

        假设有n个人,给定他们的m个朋友关系对,如果两个人是朋友,那么他们属于同一个朋友圈。请编写一个程序,输出最终每个人所属的朋友圈编号。为了解决这个问题,我们可以使用并查集数据结构。将每个人视为一个节点,朋友关系对视为边,通过合并操作将属于同一个朋友圈的人合并到同一个集合中。最后,通过查找操作确定每个人所属的朋友圈编号。代码如下。

#include <iostream>
#include <vector>
using namespace std;
class UnionFind {
private:
    vector<int> parent; // 父节点数组,初始时每个节点的父节点是自己
    vector<int> rank;   // 秩数组,记录每个节点对应的树的秩

public:
    UnionFind(int n) {
        parent.resize(n);
        rank.resize(n, 0);
        for (int i = 0; i < n; ++i) {
            parent[i] = i; // 初始化父节点为自己
        }
    }

    int find(int x) {
        if (x != parent[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) {
            if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
            } else if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
            } else {
                parent[rootY] = rootX;
                rank[rootX]++;
            }
        }
    }
};

int main() {
    int n, m;
    cin >> n >> m; // 输入人数和朋友关系对数

    UnionFind uf(n); // 初始化并查集

    for (int i = 0; i < m; ++i) {
        int x, y;
        std::cin >> x >> y; // 输入朋友关系对
        uf.unite(x - 1, y - 1); // 注意从0开始编号,需要减1转换为从1开始的编号
    }

    vector<int> circles(n); // 存储每个人所属的朋友圈编号
    for (int i = 0; i < n; ++i) {
        circles[i] = uf.find(i); // 通过查找操作确定每个人所属的朋友圈编号
        circles[i]++; // 由于我们是从0开始编号的,而题目要求从1开始编号,所以加1
    }

    // 输出每个人所属的朋友圈编号
    for (int i = 0; i < n; ++i) {
        cout << "第 " << i + 1 << " 人属于朋友圈 " << circles[i] << endl;
    }

    return 0;
}

        假设输入如下:

        4 3
        1 2
        2 3
        4 1

        表示有4个人,3对朋友关系。运行上述代码后,输出结果如下图所示.

        这表明所有人都属于同一个朋友圈,编号为1。

         通过并查集的应用,我们可以高效地解决朋友圈划分这类连通性问题。并查集通过合并和查找操作,能够快速地将元素分组,并确定元素之间的关联关系。在实际应用中,并查集还可以用于解决网络中的群组划分、图像分割等问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/640210.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】POSIX线程库——线程控制

目录 1.线程创建方法 例&#xff1a;多线程创建 2.线程终止 2.1 return nulptr; 2.2 pthread_exit(nullptr); 3. 线程等待 3.1 等待原因 3.2 等待方法 线程终止的返回值问题 4.线程取消 5. 线程分离 5.1 分离原因 5.2 分离方法 6.封装线程 用的接口是POSIX线程库…

读人工智能时代与人类未来笔记13_网络57

1. jun背控制 1.1. 威慑的目的是通过威胁发动盒站来防止盒站 1.2. jun背控制的目的是通过限制甚至废除57&#xff08;或57类别&#xff09;本身来防止盒站真 1.2.1. 与盒不扩散相配合&#xff0c;以一整套详尽的条约、技术保障措施、监管和其他控制机制为支撑&#xff0c;所…

如何生成Github Badge徽章图标

如何生成徽章Badge 什么是徽章(Badge)生成小徽章shields网站开源项目的徽章lib版本徽章代码测试覆盖度开源协议Github workflow的徽章 开源代码实践效果py-enumjs-enumerate 什么是徽章(Badge) 在开源项目的README中&#xff0c;经常会见到一些徽章(Badge)小图标&#xff0c;如…

ViLT学习

多模态里程碑式的文章&#xff0c;总结了四种多模态方法&#xff0c;根据文字和图像特征特征抽取方式不通。 文章的贡献主要是速度提高了&#xff0c;使用了数据增强&#xff0c;文本的mask 学习自b站朱老师的论文讲解

无线领夹麦克风哪个品牌好?无线麦克风品牌排行榜前十名推荐

​在当今的数字化浪潮中&#xff0c;个人声音的传播和记录变得尤为重要。无论是会议中心、教室讲台还是户外探险&#xff0c;无线领夹麦克风以其卓越的便携性和连接稳定性&#xff0c;成为了人们沟通和表达的首选工具。面对市场上琳琅满目的无线麦克风选择&#xff0c;为了帮助…

小程序多端框架目前所遇问题记录

一、wx.openLocation兼容 1、申请腾讯地图key 2、配置LBS SDK&#xff0c;选择SDK最新版本 3、调用接口&#xff0c;name和address必须输入&#xff0c;不然要报错 uni.openLocation({latitude: Number(this.info.latitude),longitude: Number(this.info.longitude),name:this…

全域外卖是谁创办的公司?

全域外卖是谁创办的公司&#xff1f;这个问题是抽象的。正确的问法应该是全域外卖是谁研发的系统。 在了解全域外卖系统前&#xff0c;我们首先要了解什么是全域外卖&#xff0c;什么是全域团购。全域指的是多平台。当然这个平台是越多越好。实际上也可以理解为聚合外卖、聚合…

Java 解决 古典问题

1 问题 编写一个Java程序&#xff0c;解决以下问题&#xff1a; 2 方法 再导入java.util包下的Scanner类&#xff0c;构建Scanner对象&#xff0c;以便输入。通过对问题的分析&#xff0c;我们可以得到&#xff0c;当位数为1时&#xff0c;其返回值为1&#xff1b;当位数为2时&…

电影推荐|基于SSM+vue的电影推荐系统的设计与实现(源码+数据库+文档)

电影推荐系统 目录 基于SSM&#xff0b;vue的电影推荐系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#…

Flutter设计模式全面解析:单例模式

谈到设计模式这个“古老”的话题&#xff0c;大家先别急着划走哈&#xff0c;虽然对它再熟悉不过&#xff0c;几乎是最初开始学习编程到现在伴随着我们整个编程生涯&#xff0c;最早 Java、C 语言实现的各种设计模式到现在还会经常有所接触&#xff0c;面试中也是必问的环节&am…

IntelliJ IDEA集成Baidu Comate,商城系统支付交易功能开发实战

文章目录 Baidu Comate介绍安装配置体验安装插件配置体验注释生成代码技术问答 实战设计表生成代码导入数据 总结 Baidu Comate介绍 在科技互联网飞速发展的今天&#xff0c;百度凭借其深厚的技术积累和创新能力&#xff0c;推出了一款名为Baidu Comate智能代码助手的产品。该…

Linxu 系统中 修改 docker 镜像存放目录 修改docker默认路径。亲测有效。

1、关闭docker 服务 systemctl stop docker 2、创建新的存放路径&#xff08;-p 父级目录不存在一起创建&#xff09; mkdir /home/service/docker -p 3、移动默认路径中的镜像文件到新目录 mv /var/lib/docker/* /home/service/docker/ 4、修改docker.service 将新的路…

【C++】继承(二)深入理解继承:派生类默认成员函数与友元、静态成员的奥秘

目录 派生类的默认成员函数①派生类的构造函数②派生类的拷贝构造函数③派生类的赋值构造④派生类的析构函数 继承与友元继承与静态成员 前言 我们在上一章讲解了: 继承三部曲&#xff0c;本篇基于上次的基础继续深入了解继承的相关知识&#xff0c;欢迎大家和我一起学习继承 派…

微信小程序报错:notifyBLECharacteristicValueChange:fail:nodescriptor的解决办法

文章目录 一、发现问题二、分析问题二、解决问题 一、发现问题 微信小程序报错&#xff1a;notifyBLECharacteristicValueChange:fail:nodescriptor 二、分析问题 这个提示有点问题&#xff0c;应该是该Characteristic的Descriptor有问题&#xff0c;而不能说nodescriptor。 …

docker-file 网络

docker挂载 1.绑定挂载&#xff08;Bind Mounts&#xff09;&#xff1a;绑定挂载是将主机上的文件或目录挂载到容器中。 docker run -v /host/path:/container/path image_name 2.卷挂载&#xff08;Volume Mounts&#xff09;&#xff1a;卷挂载将 Docker 数据卷挂载到容器中…

Java开发大厂面试第23讲:说一下 JVM 的内存布局和运行原理?

JVM&#xff08;Java Virtual Machine&#xff0c;Java 虚拟机&#xff09;顾名思义就是用来执行 Java 程序的“虚拟主机”&#xff0c;实际的工作是将编译的 class 代码&#xff08;字节码&#xff09;翻译成底层操作系统可以运行的机器码并且进行调用执行&#xff0c;这也是 …

使用delphi11编写一个基于xls作为数据库的照片展示程序

1、创建xls文档可以参考前一篇博客&#xff0c;并使用wps将文档保存为2003格式xls后缀。 2、在form上面放置adoconnection、adotable、datasource、spinedit、timer、checkbox、image、4个button组件。 image的设置&#xff1a; Image1.Align : alClient; Image1.Center : Tr…

三台泵恒压供水站电控系统及PLC程序设计实例

本文由艺捷自动化编写&#xff0c;其旗下产品有艺捷自动化网站和易为二维码说明书小程序&#xff08;微信&#xff09; 本文以一个具体的项目案例&#xff0c;来讲述一个恒压供水站的电控柜设计过程。包括用户需求&#xff0c;材料选型&#xff0c;图纸设计&#xff0c;柜内布…

Manjaro linux install RedisGUI (RedisInsight)亲测2024-5-25

Arch 用户仓库(Arch User Repository)(AUR) 是用户选择 基于 Arch Linux 的系统 的一个主要理由。你可以在 AUR 中访问到大量的附加软件。 (LCTT 译注&#xff1a;AUR 中的 PKGBUILD 均为用户上传且未经审核&#xff0c;使用者需要自负责任&#xff0c;在构建软件包前请注意检…

ubuntu 源码安装 cloudcompare

1.系统环境&#xff1a; ubuntu18 cmake&#xff1a;3.10.2 官方安装指导&#xff1a;https://github.com/CloudCompare/CloudCompare/blob/master/BUILD.md (注&#xff1a;查看cmake版本&#xff1a; cmake --version) 2.安装依赖 sudo apt-get update sudo apt-get insta…