数据结构:并查集讲解

并查集

    • 1.并查集原理
    • 2.并查集实现
    • 3.并查集应用
    • 4.并查集的路径压缩

1.并查集原理

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

如何合并两个集合

  1. 先找到两个集合的根部(负数为根部)。
  2. 以合并AB为例子,假设让B合并到A,A减去B的值,即变成-2;然后让B的值变成A的下标0
    在这里插入图片描述

观察合并过程,可以得出以下结论:

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



2.并查集实现

并查集一般可以解决以下问题:

  1. 查找元素属于哪个集合
    沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)
int FindRoot(int x)  //找根的下标位置
{int par = x;while (_ufs[par] >= 0)  //如果当前大于0,说明未到根部,更新到父亲下标{par = _ufs[par];}return par;
}

在这里插入图片描述

  1. 查看两个元素是否属于同一个集合
    沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
bool InSet(int x1, int x2)
{if (FindRoot(x1) == FindRoot(x2)){return true;}return false;
}
  1. 将两个集合归并成一个集合
    前面讲过,不赘述。
void Union(int x1, int x2) //联合这两棵树,默认x2并到x1
{int root1 = FindRoot(x1);int root2 = FindRoot(x2);if (abs(_ufs[root1]) < abs(_ufs[root2]))  //让小的一方并到大的一方就这样swap(root1, root2);if (root1 != root2)  //本来不是同一个树(集合){_ufs[root1] += _ufs[root2];_ufs[root2] = root1;}
}
  1. 集合的个数
    遍历数组,数组中元素为负数的个数即为集合的个数
int SetSize()const //返回树的个数
{int size = 0;for (auto e : _ufs)if (e < 0)  size++;return size;
}

完整实现:

class UnionFindSet {
public:UnionFindSet(size_t size)  //一开始全都设置为-1:_ufs(size, -1){}void Union(int x1, int x2) //联合这两棵树,默认x2并到x1{int root1 = FindRoot(x1);int root2 = FindRoot(x2);if (abs(_ufs[root1]) < abs(_ufs[root2]))  //让小的一方并到大的一方就这样swap(root1, root2);if (root1 != root2)  //本来不是同一个树(集合){_ufs[root1] += _ufs[root2];_ufs[root2] = root1;}}int FindRoot(int x)  //找根的下标位置{int par = x;while (_ufs[par] >= 0){par = _ufs[par];}return par;}bool InSet(int x1, int x2){if (FindRoot(x1) == FindRoot(x2)){return true;}return false;}int SetSize()const //返回树的个数{int size = 0;for (auto e : _ufs)if (e < 0)  size++;return size;}
private:vector<int> _ufs;
};



3.并查集应用

省份数量

在这里插入图片描述

//讲解:并查集,一个城市就是一个集合
//(1)初始每个城市自成一省
//(2)如果isConnected[i][j]为1,说明i城市和j城市在一个省,合并
//(3)合并完成后遍历数组,负数有几个集合(省份)就有几个
//实际并不一定要实现完整的功能,一般需要的功能是找根部函数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 root1 = FindRoot(i), root2 = FindRoot(j);if(root1 != root2)  //不是同个集合{ufs[root1] += ufs[root2];  //这一步本题没必要其实ufs[root2] = root1;}}int ret = 0;for(auto e : ufs)  if(e < 0)  ret++;return ret;}
};

等式方程的可满足性

在这里插入图片描述

//思路:并查集,先遍历一次,建立起集合联系,如果a!=b,但a与b在一个集合中,就是相悖的,返回false
// 本题只有小写字母,可用0对应a,1对应b,依次类推
class Solution {
public:bool equationsPossible(const vector<string>& equations) {vector<int> ufs(26, -1);auto FindRoot = [&ufs](int x) {while (ufs[x] >= 0){x = ufs[x];}return x;};//先遍历一次,建立并查集for (auto str : equations)if (str[1] == '='){int root1 = FindRoot(str[0] - 'a'), root2 = FindRoot(str[3] - 'a');if (root1 != root2){ufs[root1] += ufs[root2];  //这一步没必要ufs[root2] = root1;}}//再遍历一次,如果在一个集合中就返回false;for (auto str : equations)if (str[1] == '!'){int root1 = FindRoot(str[0] - 'a'), root2 = FindRoot(str[3] - 'a');if (root1 == root2){return false;}}return true;}
};



4.并查集的路径压缩

并查集一般无需压缩路径,但对数据量大的情况想加快效率就需要压缩路径

原理:

  1. 可在查询时压缩,比如6->5->4->3(根部),先找到根部3。
  2. 把6记录下来,一路向上直到根,即让6、5、4直接做3的孩子,从而完成压缩。
int FindRoot(int x)  //找根的下标位置
{int root = x;while (_ufs[root] >= 0){root = _ufs[root];}//压缩路径while (_ufs[x] >= 0){int par = _ufs[x];  //先记录父亲下标_ufs[x] = root;x = par;}return root;
}

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

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

相关文章

C语言之动态内存管理

目录 1. 为什么要有动态内存分配2. malloc和freemallocfree 3. calloc和realloccallocrealloc 4. 常见的动态内存的错误对NULL直接的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一块动态开辟内存的一部分对同一块动态内存多次释放动态开辟内存…

Github 2024-02-06 开源项目日报Top9

根据Github Trendings的统计&#xff0c;今日(2024-02-06统计)共有9个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4TypeScript项目2C项目1Ruby项目1HTML项目1Go项目1Rust项目1C项目1Kotlin项目1 Magic Mask for And…

如何从 iPhone 恢复已删除的视频:简单有效方法

无论您是在尝试释放空间时不小心删除了 iPhone 上的视频&#xff0c;还是在出厂时清空了手机&#xff0c;现在所有数据都消失了&#xff0c;都不要放弃。有一些方法可以恢复这些视频。 在本文中&#xff0c;我们将向您展示六种最有效的数据恢复方法&#xff0c;可以帮助您从 i…

图形系统开发实战课程:进阶篇(上)——3.图层类(Layer)

图形开发学院&#xff5c;GraphAnyWhere 课程名称&#xff1a;图形系统开发实战课程&#xff1a;进阶篇(上)课程章节&#xff1a;“图层类&#xff08;Layer&#xff09;”原文地址&#xff1a;https://graphanywhere.com/graph/advanced/2-3.html 第三章&#xff1a;图层类&am…

代码随想录算法训练营Day55|392.判断子序列、115.不同的子序列

目录 392.判断子序列 思路 ​算法实现 115.不同的子序列 思路 算法实现 总结 392.判断子序列 题目链接 文章链接 思路 利用动规五部曲进行分析&#xff1a; 1.确定dp数组及其下标含义&#xff1a; dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的…

寒假作业:2024/2/11

作业1&#xff1a;使用递归实现n! 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> int fun(int n) {if(0n){return 1;}else{return n*fun(n-1);} } int main(int argc, const char *argv[]) {int n;printf("please en…

深入浅出CChart 每日一课——红花当然配绿叶,CChart辅助图形绘制

各位同学&#xff0c;好久不见&#xff0c;我可想死你们了&#xff01;&#xff01;&#xff01;咦&#xff0c;那位不是巩叔吗&#xff1f;不好意思&#xff0c;侵权了&#xff0c;请多担待_。 前面的课程呢&#xff0c;拓展的内容比较多&#xff0c;最近笨笨想聚焦在CChart本…

车载电子电器架构 —— 电子电气系统功能开发

车载电子电器架构 —— 电子电气系统功能开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再挣扎,出门靠自己,四海皆…

C++三剑客之std::optional(一) : 使用详解

相关文章系列 C三剑客之std::optional(一) : 使用详解 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)&#xff1a;深入剖析 目录 1.概述 2.构建方式 2.1.默认构造 2.2.移动构造 2.3.拷贝构…

C#计算矩形面积:通过定义结构 vs 通过继承类

目录 一、涉及到的知识点 1.结构 2.结构和类的区别 3.继承 4.使用类继承提高程序的开发效率 5.属性 &#xff08;1&#xff09;属性定义 &#xff08;2&#xff09;get访问器 &#xff08;3&#xff09;set访问器 6. 属性和字段的区别 二、实例&#xff1a;通过定义…

[word] word表格表头怎么取消重复出现? #媒体#笔记#职场发展

word表格表头怎么取消重复出现&#xff1f; word表格表头怎么取消重复出现&#xff1f;在Word中的表格如果过长的话&#xff0c;会跨行显示在另一页&#xff0c;如果想要在其它页面上也显示表头&#xff0c;更直观的查看数据。难道要一个个复制表头吗&#xff1f;当然不是&…

idea:如何连接数据库

1、在idea中打开database: 2、点击 ‘’ ---> Data Source ---> MySQL 3、输入自己的账号和密码其他空白处可以不填&#xff0c;用户和密码可以在自己的mysql数据库中查看 4、最后选择自己需要用的数据库&#xff0c;点击运用ok&#xff0c;等待刷新即可 最后&#xff1a…

springboot179基于javaweb的流浪宠物管理系统的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

disql备份还原

disql备份还原 前言 本文档根据官方文档&#xff0c;进行整理。 一、概述 在 disql 工具中使用 BACKUP 语句你可以备份整个数据库。通常情况下&#xff0c;在数据库实例配置归档后输入以下语句即可备份数据库&#xff1a; BACKUP DATABASE BACKUPSET db_bak_01;语句执行完…

C++进阶(十四)智能指针

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、为什么需要智能指针&#xff1f;二、内存泄漏1、 什么是内存泄漏&#xff0c;内存泄漏的危…

量子位 | 2024年AI还能帮你干什么?这十个趋势必须关注

本文来源公众号“量子位”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;2024年AI还能帮你干什么&#xff1f;这十个趋势必须关注 大年初三&#xff0c;也不要忘记学习&#xff01;新的一年里&#xff0c;怎样能让AI多给自己帮帮…

【数据结构】13:表达式转换(中缀表达式转成后缀表达式)

思想&#xff1a; 从头到尾依次读取中缀表达式里的每个对象&#xff0c;对不同对象按照不同的情况处理。 如果遇到空格&#xff0c;跳过如果遇到运算数字&#xff0c;直接输出如果遇到左括号&#xff0c;压栈如果遇到右括号&#xff0c;表示括号里的中缀表达式已经扫描完毕&a…

每日一练:LeeCode-654、最大二叉树【二叉树+DFS+分治】

本文是力扣LeeCode-654、最大二叉树【二叉树DFS分治】 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode。 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其…

python安装cv2失败

问题:安装cv2包失败 解决方法&#xff1a; pip install opencv-python或在Anaconda中conda install opencv-python