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

news2024/10/6 1:47:39

        并查集(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。

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在XP/Vista系统下使用Node.js的babel-cli命令行工具转码ES6语法的js文件,让IE8浏览器也能运行

在XP系统下IE浏览器最高只能装到IE8&#xff0c;在Vista系统下最高只能装到IE9。 2015年以后&#xff0c;JavaScript新增了很多语法&#xff0c;比如class、extends&#xff0c;还有let和const等等&#xff0c;这些语法都是XP下的终端浏览器IE8所不支持的。要想让使用了这些新式…

[集群聊天服务器]----(五)User类、UserModel类

接着上文[集群聊天服务器]----(四)MySQL数据库模块&#xff0c;接下来我们对User类、UserModel类进行剖析&#xff0c;User表和UserModel类是项目最基本也是最重要的部分&#xff0c;通过它我们对用户的id&#xff0c;用户名&#xff0c;密码&#xff0c;状态相关信息进行存储&…

VSCode运行CMake教程

参考视频&#xff1a; https://www.youtube.com/watch?vm9HBM1m_EMU 首先下载&安装opencv包以及cmake包 接着配置环境变量 点击环境变量 ---下面系统变量的path 找对应路径 添加后三个: 确定即可 接着创建空目录 右击vscode打开 扩展中安装 c tools、cmake、cmake too…

华为芯片与系统详细梳理--Kirin麒麟 Ascend昇腾 Kunpeng鲲鹏 HarmonyOS鸿蒙 Euler欧拉

华为芯片与系统详细梳理--Kirin麒麟 & Ascend昇腾 & Kunpeng鲲鹏 & HarmonyOS鸿蒙 & Euler欧拉 1 概述2 芯片2.1 整体描述麒麟芯片&#xff08;To C&#xff09;【面向智能终端】昇腾芯片【面向AI计算】鲲鹏芯片【面向通用计算】 2.2 细分系列麒麟芯片&#xf…

基于UDP的tftp的文件传输

#define SER_PORT 69 #define SER_IP "192.168.125.71" #define CLT_PORT 6666 #define CLT_IP "192.168.125.158" int main(int argc, const char *argv[]) {//创建套接字文件描述符int cfd socket(AF_INET,SOCK_DGRAM,0);if(cfd -1){perror("sock…

Hadoop3:HDFS的Fsimage和Edits文件介绍

一、概念 Fsimage文件&#xff1a;HDFS文件系统元数据的一个永久性的检查点&#xff0c;其中包含HDFS文件系统的所有目 录和文件inode的序列化信息。 Edits文件&#xff1a;存放HDFS文件系统的所有更新操作的路径&#xff0c;文件系统客户端执行的所有写操作首先 会被记录到Ed…

掌握RESTful API:从入门到精通,全面解析Web开发的基石!

在现代Web开发中&#xff0c;API&#xff08;应用程序编程接口&#xff09;已经成为不同系统之间通信的重要手段。其中&#xff0c;RESTful API是一种基于HTTP协议的设计风格&#xff0c;它简洁、易用且高效。作为一个资深的技术人员&#xff0c;本文将全面详细地介绍RESTful A…

VM逆向,一篇就够了(下)

实战三 d3sky 这一题需要了解一些TLS相关知识 TLS回调函数 TLS&#xff08;Thread Local Storage&#xff0c;线程局部存储&#xff09;是各线程的独立的存储空间&#xff0c;TLS回调函数是指&#xff0c;每当创建或终止进程的线程时会自动调用执行的函数&#xff0c;且调用…

蓝桥杯Web开发【国赛】2022年真题

1.水果拼盘 目前 CSS3 中新增的 Flex 弹性布局已经成为前端页面布局的首选方案&#xff0c;本题可以使用 Flex 属性快速完成布局。 1.1 题目问题 建议使用 flex 相关属性完成 css/style.css 中的 TODO 部分。 禁止修改圆盘的位置和图片的大小。相同颜色的水果放在相同颜色的…

[Unity报错] The type or namespace name ‘Newtonsoft‘ could not be found

简介 Unity在跑别人的代码时&#xff0c;控制台报了以下错误 The type or namespace name Newtonsoft could not be found 鉴于这块资料较少&#xff0c;写一下教程帮助后来者。 报错的原因主要是因为缺少Newtonsoft.json这个包&#xff0c;导致Unity在using该库时出现错误。…

振动盘配上智能相机后实现全自动化

#智能相机 #振动盘 振动盘配上智能相机后的全自动化技术正在各个行业蓬勃发展。振动盘是一种利用震动作用将物料快速分离和输送的装置&#xff0c;而智能相机的高精度图像识别技术能够准确探测和捕捉物料。将两者结合起来&#xff0c;不仅能提高生产效率&#xff0c;还能降低人…

AI爆文写作:标题需要什么?情绪炸裂,态度要激烈,行为要夸张!

现在这个传播环境下&#xff0c;在公域中&#xff0c;轻声细语&#xff0c;慢慢的说&#xff0c;无法吸引到注意&#xff0c;没有人搭理。 标题要需要情绪张扬&#xff0c;态度激烈&#xff0c;行为夸张&#xff0c;大声喧闹。 唐韧的用户群是互联网产品经理&#xff0c;阅读量…

开源实用!猫抓媒体嗅探浏览器插件

CatCatch&#xff1a;网络资源&#xff0c;一触即发 - 精选真开源&#xff0c;释放新价值。 概览 CatCatch是一个专为浏览器设计的资源嗅探扩展&#xff0c;旨在帮助用户轻松捕获和分析网页中的各种资源。无论是视频、音频还是其他类型的文件&#xff0c;猫爪都能提供直观的界…

了解K8s集群kubectl命令进行陈述式资源管理

前言 在 Kubernetes 集群中&#xff0c;通过陈述式和声明式资源管理是确保应用程序高效运行的关键。认识这两种管理方法&#xff0c;能够更好地掌握 Kubernetes 集群的运维和管理。 目录 一、K8s 资源管理操作分类 1. 陈述式 2. 声明式 3. K8s 集群管理常用命令概览 二…

五分钟”手撕“图书管理系统

前言&#xff1a; 图书馆管理系统需要结合JavaSE的绝大部分知识&#xff0c;是一个很好的训练项目。 为了让大家更加方便的查阅与学习&#xff0c;我把代码放开头&#xff0c;供大家查询。 还有对代码的分析&#xff0c;我将以类为单位分开讲解。 目录 全部代码 Main类 Us…

【NumPy】NumPy实战入门:线性代数(dot、linalg)与随机数(numpy.random)详解

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

redis--redis Cluster

简介 解决了redis单机写入的瓶颈问题&#xff0c;即单机的redis写入性能受限于单机的内存大小、并发数量、网卡速率等因素无中心架构的redis cluster机制&#xff0c;在无中心的redis集群当中&#xff0c;其每个节点保存当前节点数据和整个集群状态,每个节点都和其他所有节点连…

高阶路由过渡处理方案 —— 浏览器堆栈主动介入

目录 01: 前言 02: VueRouter 过渡动效可行性分析 03: 主动介入浏览器堆栈管理&#xff0c;分析可行性方案 04: 主动介入浏览器堆栈管理 05: 基于 GSAP 实现高阶路由过渡动画分析 06: 基于 GSAP 实现高阶路由过渡动画处理 07: 通用组件&#xff1a;navbar 构建方案分析…

202206青少年软件编程(Python)等级考试试卷(三级)

第 1 题 【单选题】 下图所示, 有一个名为"书目.csv"的文件。 小明针对这个文件编写了 5 行代码,请问, 代码运行到最后打印在屏幕上的结果是? ( ) with open(书目.csv, r, encoding=utf-8) as f:for line in f.readlines

Rabbitmq-Windows 安装

第一步&#xff1a;下载并安装erlang &#xff11;.原因&#xff1a;RabbitMQ服务端代码是使用并发式语言Erlang编写的&#xff0c;安装Rabbit MQ的前提是安装Erlang &#xff12;.下载地址&#xff1a;http://www.erlang.org/downloads 3.双击&#xff0c;点next就可以 4.选…