【高阶数据结构】并查集 {并查集原理;并查集优化;并查集实现;并查集应用}

一、并查集原理

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

比如:某公司今年校招全国总共招生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担任队长,负责大家的出行。

在这里插入图片描述

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

在这里插入图片描述

从上图可以看出:编号6,7,8同学属于0号小分队,该小分队中有4人(包含队长0);编号为4和9的同学属于1号小分队,该小分队有3人(包含队长1),编号为3和5的同学属于2号小分队,该小分队有3个人(包含队长1)。

仔细观察数组中的内容,可以得出以下结论:

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

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

在这里插入图片描述

现在0集合有7个人,2集合有3个人,总共两个朋友圈。
通过以上例子可知,并查集一般可以解决一下问题:

  1. 查找元素属于哪个集合
    沿着数组表示的树形关系向上一直找到根 (即:树中中元素为负数的位置)
  2. 查看两个元素是否属于同一个集合
    沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
  3. 将两个集合归并成一个集合
    找到两个集合的根
    一个根任作为根,另一个作孩子:作根的将两集合的元素个数加到一起,作孩子的,将值变为根在数组中的下标
  4. 集合的个数
    遍历数组,数组中元素为负数的个数即为集合的个数。

二、并查集优化

  1. 按秩合并
    按秩合并是并查集的一种优化方法,其核心思想是在合并两个集合时,将秩(可以理解为树的高度)较小的节点的根直接接到秩较大的节点的根上,而不必在寻找秩较大的节点的根的过程中进行额外的find函数调用。这样做的好处是可以减少find函数的调用次数,从而优化算法的效率。
    我们用一个数组rank[]记录每个根节点对应的树的深度(如果不是根节点,其rank相当于以它作为根节点的子树的深度)。一开始,把所有元素的rank(秩)设为1。合并时比较两个根节点,把rank较小者往较大者上合并。具体来说,如果两个集合的秩不同,那么秩较小的集合的根节点会成为秩较大的集合的子节点。如果两个集合的秩相同,那么在合并时,秩较大的集合的根节点的秩会增加1。
    按秩合并的好处包括:
    降低树的高度:按秩合并优化了合并操作,使得树的高度相对较小,从而减少了查找操作的时间复杂度。
    提高查找操作效率:通过按秩合并,将较小的树合并到较大的树上,减少树的高度,从而减少了查找操作的路径长度,提高了查找效率。
    平衡树的结构:按秩合并能够保持树结构的平衡性,避免树退化成链表,进一步提高了查找操作的效率。

  2. 路径压缩
    然而,需要注意的是,按秩合并虽然在一定程度上优化了并查集的性能,但在实际应用中,路径压缩技术往往能带来更大的性能提升。路径压缩是一种在查找节点根节点的同时,将查找路径上的节点的父节点直接设置为根节点的方法,这样可以使得树的深度大大降低,从而在后续的操作中提高效率。

  3. 路径压缩和按秩合并如果一起使用,很可能会破坏rank的准确性。

  4. 算法学习笔记:并查集的优化详解


二、并查集实现

#include <vector>
#include <map>
using namespace std;// 只有编号,实现一个简单的并查集
class UnionFindSet{vector<int> _ufs; //并查集spublic:UnionFindSet(int n){_ufs.resize(n, -1); //初始时,每个元素自成一个单元素集合}bool Union(int x, int y){int rx = FindRoot(x);int ry = FindRoot(y);if(rx == ry) return false;_ufs[rx] += _ufs[ry];_ufs[ry] = rx;return true;}int FindRoot(int x){int root = x;while(_ufs[root] >= 0){root = _ufs[root];}// 路径压缩while(_ufs[x] >= 0){int parent = _ufs[x];_ufs[x] = root;x = parent;}return root;}bool InSet(int x, int y){return FindRoot(x)==FindRoot(y);}int SetCount(){int count = 0;for(int e : _ufs){if(e < 0) ++count;}return count;}
};// 其他数据类型组成的并查集
// 1.建立编号和其他数据类型相互的映射关系
// 2.最终还是要通过编号组织并查集
// template <class T>
// class UnionFindSet{
//     vector<T> _vct; //编号找人
//     map<T, int> _map; //人找编号
//     vector<int> _ufs; //并查集s
// public:
//     UnionFindSet(const T* set, int n)
//     {
//         //1.建立编号<-->人名的映射关系
//         for(int i = 0; i < n; ++i)
//         {
//             _vct.push_back(set[i]);
//             _map[set[i]] = i;
//         }
//         //2.初始化并查集
//         _ufs.resize(n, -1); //初始时,每个元素自成一个单元素集合
//     }
// };

三、并查集应用

LCR 116. 省份数量 - 力扣(LeetCode)

class Solution {
public:int findCircleNum(vector<vector<int>>& isConnected) {vector<int> ufs(isConnected.size(), -1);auto FindRoot = [&ufs](int x){while(ufs[x] >= 0) x = ufs[x];return x;};for(int i = 0; i < isConnected.size(); ++i){for(int j = 0; j < isConnected[i].size(); ++j){if(isConnected[i][j] == 1) {int r1 = FindRoot(i);int r2 = FindRoot(j);if(r1 != r2){ufs[r1] += ufs[r2];ufs[r2] = r1;}}}}int count = 0;for(int e : ufs){if(e < 0) ++count;}return count;}
};

990. 等式方程的可满足性 - 力扣(LeetCode)

class Solution {
public:bool equationsPossible(vector<string>& equations) {vector<int> ufs(26, -1);auto FindRoot = [&ufs](int x){while(ufs[x] >= 0) x = ufs[x];return x;};for(auto& e : equations){if(e[1] == '='){int r1 = FindRoot(e[0]-'a');int r2 = FindRoot(e[3]-'a');if(r1 != r2){ufs[r1] += ufs[r2];ufs[r2] = r1;}}}for(auto& e : equations){if(e[1] == '!'){int r1 = FindRoot(e[0]-'a');int r2 = FindRoot(e[3]-'a');if(r1 == r2) return false;}}return true;}
};

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

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

相关文章

构建NFS远程共享存储

目录 一. NFS介绍 二. 网络附加存储&#xff08;NAS&#xff09;设备 三. 远程过程调用 四. 实验测试 4.1 nfs-server操作 4.1.1 新建一个目录作为共享 4.1.2 新增一个磁盘作为共享 4.2 web1 客户端操作 一. NFS介绍 NFS&#xff08;Network File System&#xff09…

UTONMOS探索元宇宙:开启未来数字世界的无限可能

在科技的浪潮中&#xff0c;元宇宙如同一颗璀璨的星辰&#xff0c;冉冉升起&#xff0c;吸引着无数人的目光。 元宇宙&#xff0c;一个超越现实的数字世界&#xff0c;它融合了虚拟现实、增强现实和互联网等多种技术&#xff0c;为人们打造了一个全新的沉浸式体验空间。在这里…

数据结构--顺序表和链表的区别

顺序表和链表之间各有优劣&#xff0c;我们不能以偏概全&#xff0c;所以我们在使用时要关注任务的注重点&#xff0c;以此来确定我们要使用两者中的哪一个。 不同点&#xff1a; 存储空间上&#xff1a; 顺序表在物理结构上是一定连续的&#xff0c;而链表(这里以带头双向循环…

面 试 题

过滤器和拦截器的区别 都是 Aop 思想的一种体现&#xff0c;用来解决项目中 某一类 问题的两种接口(工具)&#xff0c;都可以对请求做一些增强 出身 过滤器来自 servlet 拦截器来自 spring 使用范围 过滤器 Filter 实现了 iavax.servlet.Filter 接口&#xff0c;也就是说…

CSS实现渐变色

渐变色分为线性渐变和径向渐变。 线性渐变linear-gradient(方向, 颜色1, 颜色2, … ,颜色n)径向渐变radial-gradient(颜色1 覆盖区域大小, 颜色2 覆盖区域大小, … ) 线性渐变的方向可以为&#xff1a; ​ 1、一个方向值时&#xff1a; to bottom 表示从上边到下边渐变 ​ 2、…

Spring AI多模态接口开发

文章目录 项目地址创建项目配置项目接口开发结果测试测试接口测试在线图片接口测试本地图片接口测试 项目地址 Spring AI项目开发 创建项目 打开IDEA创建一个新的spring boot项目&#xff0c;填写项目名称和位置&#xff0c;类型选择maven&#xff0c;组、工件、软件包名称可…

Android 11 输入系统之InputDispatcher和应用窗口建立联系

InputDispatcher把输入事件传给应用之前&#xff0c;需要和应用窗口建立联系&#xff0c;了解了这个过程&#xff0c;就清楚了APP进程和InputDispatcher线程也就是SystemServer进程之间是如何传输数据了 我们向窗口addView的时候&#xff0c;都会调用到ViewRootImpl的setView方…

Docker 部署 Nginx 实现一个极简的 负载均衡

背景: Nginx是异步框架的网页服务器&#xff0c;其常用作反向代理(负载均衡器)。在一般的小项目中, 服务器不多, 如果不考虑使用服务注册与发现, 使用Nginx 可以容易实现负载均衡。 在特此写一个快速入门 Nginx 的技术贴, 使用 Docker 部署 Nginx, 实现一个极简的加权轮询负载均…

现在的原创内容博客 SEO 最好就选谷歌和必应!

当我们在国内讨论搜索引擎优化的时候&#xff0c;我们经常讨论的是百度 SEO&#xff0c;很少提及 Bing 搜索与 Google 搜索&#xff0c;但随着跨境电商的崛起&#xff0c;在国内做外贸 SEO 的小伙伴越来越多&#xff0c;有效的了解 Bing 搜索与 Google 搜索的优化规则是很有必要…

开源aodh学习小结

1 介绍 aodh是openstack监控服务&#xff08;Telemetry&#xff09;下的一个模块&#xff0c;telemetry下还有一个模块ceilometer OpenStack Docs: 2024.1 Administrator Guides Get Started on the Open Source Cloud Platform - OpenStack Telemetry - OpenStack 1.1 代码仓…

softmax函数与交叉熵损失详解

文章目录 一、softmax函数1.1 引入指数形式的优点1.2 引入指数形式的缺点 二、交叉熵损失函数2.1 交叉熵损失函数2.2 softmax与交叉熵损失 参考资料 一、softmax函数 softmax用于多分类过程中&#xff0c;它将多个神经元的输出&#xff0c;映射到&#xff08;0,1&#xff09;区…

【C++ 内存管理】深拷贝和浅拷贝你了解吗?

文章目录 1.深拷贝2.浅拷贝3.深拷贝和浅拷贝 1.深拷贝 &#x1f34e; 深拷⻉: 是对对象的完全独⽴复制&#xff0c;包括对象内部动态分配的资源。在深拷⻉中&#xff0c;不仅复制对象的值&#xff0c;还会复制对象所指向的堆上的数据。 特点&#xff1a; &#x1f427;① 复制对…

记录一下 log4j的漏洞

目录 背景 bug的产生 bug复现 JNDI 网络安全学习路线 &#xff08;2024最新整理&#xff09; 学习资料的推荐 1.视频教程 2.SRC技术文档&PDF书籍 3.大厂面试题 特别声明&#xff1a; 背景 log4j这次的bug&#xff0c;我相信大家都已经知道了&#xff0c;仅以…

【unity小技巧】减少Unity中的构建打包大小

文章目录 正常默认打包查看编辑器打包日志压缩图片压缩网格模型压缩贴图压缩音频文件只打64位包最终大小完结 正常默认打包 这里以安卓为例。先什么都不干&#xff0c;直接打包安卓apk&#xff0c;查看包大小 查看编辑器打包日志 搜索build report构建报告。构建报告我们应该…

Pytorch学习-引言

Pytorch相关链接 Pytorch官方网站 https://pytorch.org/ Pytorch的Github仓库 https://github.com/pytorch/pytorch Pytorch论坛 https://discuss.pytorch.org/ Pytorch离线下载包链接 https://download.pytorch.org/whl/torch_stable.html Pytorch学习视频推荐链接 http://【…

手写一个SPI FLASH 读写擦除控制器

文章目录 flash读写数据的特点1. 扇擦除SE&#xff08;Sector Erase&#xff09;1.1 flash_se 模块设计1.1.1 信号连接示意图&#xff1a;1.1.2 SE状态机1.1.3 波形图设计&#xff1a;1.1.4 代码 2. 页写PP(Page Program)2.1 flash_pp模块设计2.1.1 信号连接示意图&#xff1a;…

JavaScript 对象入门:基础用法全解析

目录 对象 语法 属性和访问 方法和调用 this关键字 null 遍历对象 内置对象 Math 属性 方法 Date 创建日期对象 获取和设置日期 ⭐对象 对象是 JavaScript 数据类型的一种&#xff0c;数据类型也包括数值类型、字符串类型、布尔类型、undefined。对象数据类型可…

程序员之路:裁员与内卷下的生存之道

作为一名普通的程序员&#xff0c;身处这个瞬息万变的IT行业&#xff0c;面对着今年不断加剧的裁员浪潮和日益激烈的内卷竞争&#xff0c;我时常感到焦虑和不安。然而&#xff0c;正是这些挑战&#xff0c;让我们更加深入地思考了在这个行业中&#xff0c;我们该如何找到自己的…

2024统计建模中国新质生产力统计测度与时空演变及其驱动因素研究

高质量成品论文46页word版本1.5w字书写完整数据集1000行py代码一等奖论文&#xff01;这里仅展示部分内容&#xff0c;完整版在下面的链接。 【1.5w字全网最佳】2024统计建模大赛高质量成品论文39页配套完整代码运行全套数据集https://www.jdmm.cc/file/2710661/ 中国新质生产…

【2024HNCTF】密码组部分出题记录

2024H&NCTF 密码组部分出题记录 题目&#xff1a;BabyPQ、HappyDance 文章目录 2024H&NCTF 密码组部分出题记录BabyPQ | 签到HappyDance BabyPQ | 签到 本题为nc交互题&#xff0c;之所以采用这种形式&#xff0c;是因为可能有很多密码新师傅们不了解这种赛题形式&a…