[高阶数据结构(一)]并查集详解

news2024/11/23 17:29:30

1.前言

本系列会带大家走进高阶数据结构的学习, 其中包括并查集,图论, LRU cache, B树, B+树, B*树, 跳表. 其中, 图论中讲解的时间最长, 包括邻接表, 邻接矩阵, 广度优先遍历, 深度优先遍历, 最小生成树中的kruskal算法以及prim算法;最短路径中的dijkstra算法, bellman-Ford算法, Floyd-wars hall算法. 

本章重点

本章着重讲解并查集的原理、并查集的模拟实现以及并查集的相关应用。

2.并查集的原理

在一些应用问题中,需要 n 个不同的元素划分成一些不相交的集合 开始时,每个元素自成一个 单元素集合,然后按一定的规律将归于同一组元素的集合合并 。在此过程中 要反复用到查询某一 个元素归属于那个集合的运算 。适合于描述这类问题的抽象数据类型称为 并查集 (union-find set)
举例说明:
比如:某公司今年校招全国总共招生 10 人,南京招 4 人,杭州招 3 人,北京招 3 人, 10 个人来自不
同的学校,起先互不相识,每个学生都是一个独立的小团体,现给这些学生进行编号: {0, 1, 2, 3,
4, 5, 6, 7, 8, 9}; 给以下数组用来存储该小集体,数组中的数字代表:该小集体中具有成员的个
数。 (为什么全部初始化为-1呢? 负号下文解释 )
假设南京学生:0 ,6,7,8;他们通过某种方式认识了,于是就组团从南京出发去该公司。
        杭州学生:1,4,9;他们也通过某种方式相互认了,于是也组团从杭州出发去该公司。
        北京学生:2.3.5;和南京、杭州学生同理。
于是他们分别选择了自己团队里面的社牛(0,1,2)来当组长,方便后续认识新的同学。
但是怎么用数组来表示出这些树的集合呢?---通过如下方法
解释:数组中下标对应的-n,负数就表示他是一个集合的组长,n就表示他这个集合有多少人。对于是组长的人来说,那就存储一个负数,而对于非组长的人来说下标里面的值就存储的是组长所在位置的下标。
南京小组的组长是0,它的值是-4代表此小组有四个人.6,7,8是0的组员,所以它们存储的值是0,也就是组长的下标. 所以刚开始初始值为-1,代表每一个数都自成一个集合

 那么此时非常凑巧的是,南京和杭州的同学坐在同一辆车上去往该公司,且座位还比较靠近。在坐车闲聊的过程中,相互都认识了,于是就想着将两个集合合并成一个集合,于是就出现了下面的情况

此时,下标为1的位置应该存储它的父亲,也就是0,下标为4.9的位置不能直接存储0,而是应该存储1,因为1才是他们的直系父亲. 可以用下图来表示:

从这里就可以发现了,下标1所在位置的值发生了改变,原来三个集合合并成了两个集合。

0所在的下标由原来的-4变成了-7,而1所在下标的值由-3变成了0。

3.并查集的模拟实现

通过观察上述过程,可以知道你至少要有下面几个函数做支撑:找到一个下标的根(后续合并的操作由根部对应的下标值进行改变), 合并两个集合, 判断两个树是否在同一个集合。

由于并查集的本质是一个数组,所以基本框架如下:

class UnionFindSet
{
public:
	UnionFindSet(size_t n)
		:_ufs(n, -1)//直接先开辟n个空间,负数表示根,1表示一个节点连接自己
	{}
	//合并并查集
	void Union(int x1, int x2)
	{}
	//查找某个值下标的根下标在哪
	int FindParent(int x)
	{}
	//有几个并查集
	size_t SetSize()
	{}
	//判断两个下标所在的值是否在同一个并查集
	bool SameSet(int x, int y)
	{}
private:
	vector<int> _ufs;
};

首先就是要先写出找到一个下标的根的函数,如下所示

//查找某个值下标的根下标在哪
int FindParent(int x)
{
	int parent = x;
	while (_ufs[parent] >= 0)
		parent = _ufs[parent];
	//如果合并太多,可能导致并查集太高,所以通过压缩高度,修改合并之后某些值的父亲指向
	while (_ufs[x] >= 0)
	{
		int tmp = _ufs[x];
		_ufs[x] = parent;//直接指根节点
		x = tmp;
	}
	return parent;
}

代码分为两步,第一步就是在找它的根,就是一直向前找直到遇见负数.第二部分的代码在进行路径压缩工作,若是有多个集合进行合并,那么我们的树可能就会很高,查找最下面的树的根时,就会出现效率低下的问题,所以进行路径压缩很有必要. 关于路径压缩的原理如下:

当我下次找4的时候,时间就变少了。

接下来就是合并两个并查集的代码:(数据少的集合,向数据多的集合里面合并)

//合并并查集
void Union(int x1, int x2)
{
	int root1 = FindParent(x1);
	int root2 = FindParent(x2);
	if (root1 == root2) return;
	if (abs(_ufs[root1]) >= abs(_ufs[root2]))
	{
		_ufs[root1] += _ufs[root2];
		_ufs[root2] = root1;
	}
	else
	{
		_ufs[root2] += _ufs[root1];
		_ufs[root1] = root2;
	}
}

最后就是告诉外部,现在有几个集合的代码了

//有几个并查集
size_t SetSize()
{
	size_t n = 0;
	for (int i = 0; i < _ufs.size(); i++)
	{
		if (_ufs[i] < 0)
			n++;
	}
	return n;
}

最后给出整体代码,相关注释均在里面,不懂的小伙伴可以后台T我

class UnionFindSet
{
public:
	UnionFindSet(size_t n)
		:_ufs(n, -1)//直接先开辟n个空间,负数表示根,1表示一个节点连接自己
	{}
	//合并并查集
	void Union(int x1, int x2)
	{
		int root1 = FindParent(x1);
		int root2 = FindParent(x2);
		if (root1 == root2) return;
		if (abs(_ufs[root1]) >= abs(_ufs[root2]))
		{
			_ufs[root1] += _ufs[root2];
			_ufs[root2] = root1;
		}
		else
		{
			_ufs[root2] += _ufs[root1];
			_ufs[root1] = root2;
		}
	}
	//查找某个值下标的根下标在哪
	int FindParent(int x)
	{
		int parent = x;
		while (_ufs[parent] >= 0)
			parent = _ufs[parent];
		//如果合并太多,可能导致并查集太高,所以通过压缩高度,修改合并之后某些值的父亲指向
		while (_ufs[x] >= 0)
		{
			int tmp = _ufs[x];
			_ufs[x] = parent;//直接指根节点
			x = tmp;
		}
		return parent;
	}
	//有几个并查集
	size_t SetSize()
	{
		size_t n = 0;
		for (int i = 0; i < _ufs.size(); i++)
		{
			if (_ufs[i] < 0)
				n++;
		}
		return n;
	}
	//判断两个下标所在的值是否在同一个并查集
	bool SameSet(int x, int y)
	{
		return FindParent(x) == FindParent(y);
	}
private:
	vector<int> _ufs;
};

4.并查集的应用

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

这道问题就是在问你,有几个集合。

不多说直接上代码

class UnionFindSet
{
private:
    vector<int> _ufs;
public:
    UnionFindSet(size_t n)
        :_ufs(n,-1)
        {}
    int FindRoot(const int& val)
    {
        int root=val;
        while(_ufs[root]>=0)
            root=_ufs[root];
        return root;
    }
    void Union(int x,int y)
    {
        int root1=FindRoot(x);
        int root2=FindRoot(y);
        if(root1==root2) return;
        _ufs[root1]+=_ufs[root2];
        _ufs[root2]=root1;
    }
    int SetSize()
    {
        int ret=0;
        for(auto e:_ufs)
            if(e<0) ret++;
        return ret;
    }
};
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n=isConnected.size();
        UnionFindSet ufs(n);
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                if(isConnected[i][j]) ufs.Union(i,j);
            }
        }
        return ufs.SetSize();
    }
};

并查集的题目还有很多,这里就不一 一列举了,有兴趣可以去牛客,leedcode上面看看。 

5.总结

并查集相对来说是属于高阶数据结构里面比较简单的了,但是也是比较重要的一个部分。在求职过程中也会经常遇到并查集相关的考点,希望各位小伙伴能够好好学习,打下坚实的基础。

 

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

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

相关文章

应聘美容师要注意什么?博弈美业收银系统/管理系统/拓客系统分享建议

随着美容行业的不断发展&#xff0c;成为一名优秀的美容师需要具备一系列重要的技能和品质。无论是在面试过程中还是在实际工作中&#xff0c;以下建议将帮助你在应聘美容师职位时脱颖而出&#xff1a; ▶ 专业技能和资格 首先&#xff0c;确保你具备所需的专业技能和资格。这…

el-cascader 使用笔记

1.效果 2.官网 https://element.eleme.cn/#/zh-CN/component/cascader 3.动态加载&#xff08;官网&#xff09; <el-cascader :props"props"></el-cascader><script>let id 0;export default {data() {return {props: {lazy: true,lazyLoad (…

vmWare虚拟环境centos7安装Hadoop 伪分布式实践

背景&#xff1a;近期在研发大数据中台&#xff0c;需要研究Hadoop hive 的各种特性&#xff0c;需要搭建一个Hadoop的虚拟环境&#xff0c;本来想着使用dock &#xff0c;但突然发现docker 公共仓库的镜像 被XX 了&#xff0c;无奈重新使用vm 搭建虚拟机。 大概经历了6个小时完…

Redis基本的全局命令

在学习redis基本的全局命令之前呢&#xff0c;我们必须先进入redis-cli客户端才行。 如图&#xff1a; get和set get和set是redis两个最核心的命令。 get&#xff1a;根据key来获取value。 set&#xff1a;把key和value存储进去。 如set命令如图&#xff1a; 对于上述图中&…

Redis五大基本类型——List列表命令详解(命令用法详解+思维导图详解)

目录 一、List列表类型介绍 二、常见命令 1、LPUSH 2、LPUSHX 3、RPUSH 4、RPUSHX 5、LRANGE 6、LPOP 7、RPOP 8、LREM 9、LSET 10、LINDEX 11、LINSERT 12、LLEN 13、阻塞版本命令 BLPOP BRPOP 三、命令小结 相关内容&#xff1a; Redis五大基本类型——Ha…

一文详解哋它亢模块的安装与使用

如何安装哋它亢模块 哋它亢模块是扩展哋它亢功能的关键工具&#xff0c;它们涵盖了从数据分析到机器学习的各种应用场景。通过安装和使用这些模块&#xff0c;你可以轻松完成复杂的任务&#xff0c;大幅提升开发效率。哋它亢是一门易于学习且功能强大的编程语言&#xff0c;以…

C#中的二维数组的应用:探索物理含义与数据结构的奇妙融合

在C#编程中&#xff0c;二维数组&#xff08;或矩阵&#xff09;是一种重要的数据结构&#xff0c;它不仅能够高效地存储和组织数据&#xff0c;还能通过其行、列和交叉点&#xff08;备注&#xff1a;此处相交处通常称为“元素”或“单元格”&#xff0c;代表二维数组中的一个…

论文阅读——Intrusion detection systems using longshort‑term memory (LSTM)

一.基本信息 论文名称&#xff1a;Intrusion detection systems using longshort‑term memory (LSTM) 中文翻译&#xff1a;基于长短期记忆(LSTM)的入侵检测系统 DOI&#xff1a;10.1186/s40537-021-00448-4 作者&#xff1a;FatimaEzzahra Laghrissi1* , Samira Douzi2*, Kha…

【行之有效】实证软件工程研究方法

【行之有效】实证软件工程研究方法 一、实证研究二、实证软件工程2.1 系统化文献评价2.2 调查研究2.2.1 数据收集2.2.2 抽样 2.3 案例研究2.3 实证研究效度 一、实证研究 实证研究&#xff08;Empirical Research&#xff09;方法是一种与规范研究&#xff08;Normative Resea…

大数据挖掘期末复习

大数据挖掘 数据挖掘 数据挖掘定义 技术层面&#xff1a; 数据挖掘就是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中&#xff0c;提取隐含在其中、人们事先不知道的、但又潜在有用的信息的过程。 数据准备环节 数据选择 质量分析 数据预处理 数据仓库 …

河道水位流量一体化自动监测系统:航运安全的护航使者

在广袤的水域世界中&#xff0c;航运安全始终是至关重要的课题。而河道水位流量一体化自动监测系统的出现&#xff0c;如同一位强大的护航使者&#xff0c;为航运事业的稳定发展提供了坚实的保障。 水位传感器&#xff1a;负责实时监测河道的水位变化。这些传感器通常采用先进的…

【C++】深入理解 C++ 中的继承进阶:多继承、菱形继承及其解决方案

个人主页: 起名字真南的CSDN博客 个人专栏: 【数据结构初阶】 &#x1f4d8; 基础数据结构【C语言】 &#x1f4bb; C语言编程技巧【C】 &#x1f680; 进阶C【OJ题解】 &#x1f4dd; 题解精讲 目录 C继承机制详解与代码示例&#x1f4cc;1. 继承的基本概念&#x1f4cc; 2.…

根据条件 控制layui的table的toolbar的按钮 显示和不显示

部分代码&#xff1a; <!-----查询条件-----> <input type"date" id"StartDate" onchange"PageList()" /> <input type"date" id"EndDate" onchange"PageList()" /><!-----表格Table-----&…

net某高校社交学习平台的设计与实现

摘 要 高校社交学习平台是一个融合了社交网络特性的在线学习交流系统&#xff0c;旨在促进高校学生之间的信息共享与学习互动。该平台通过提供学习资料、学习视频和学习交流等功能&#xff0c;支持发布学习动态、参与知识问答、并实时追踪学习进度。为学生提供一个全面且便捷的…

5-对象的访问权限

对象的访问权限知识点 对象的分类 在数据库中&#xff0c;数据库的表、索引、视图、缺省值、规则、触发器等等、都可以被称为数据库对象&#xff0c;其中对象主要分为两类 1、模式(schema)对象&#xff1a;模式对象可以理解为一个存储目录、包含视图、索引、数据类型、函数和…

如何在vscode 中打开新文件不覆盖上一个窗口

在 VSCode 中&#xff0c;如果你单击文件时出现了覆盖Tab的情况&#xff0c;这通常是因为VSCode默认开启了预览模式。在预览模式下&#xff0c;单击新文件会覆盖当前预览的文件Tab。为了解决这个问题&#xff0c;你可以按照以下步骤进行操作 1.打开VSCode&#xff1a;启动你的…

【网络系统管理】Centos7——配置主从mariadb服务器案例(下半部分)

【网络系统管理】Centos7——配置主从mariadb服务器案例-CSDN博客 接上个文档&#xff0c;我们已经完成了主服务器创建数据库备服务器可以看到 一、在DBMS2查看信息 File&#xff0c;Position这两个字段的数据要记好&#xff0c;等一下需要用到 show master status; 二、在…

C#编写的日志记录组件 - 开源研究系列文章

以前编写过一个日志记录组件的博文&#xff0c;这次发布一个修改过的完善版本。 1、 项目目录&#xff1b; 2、 源码介绍&#xff1b; 1) 实现&#xff1b; 2) 使用&#xff1b; 后面的参数为级别设置&#xff0c;只有大于这个级别的才进行日志记录&#xff0c;限制了日志记录的…

react 如何修改弹出的modal的标题

原来标题的样子&#xff1a; 修改为&#xff1a; 实现方式&#xff1a; <Modal title<span>股价趋势/{this.state.pccode}</span> visible{this.state.isPriceModalOpen} style{{ top: 20 }} width{1320} height{400} footer{null} onCancel{()>this.hideMo…

学习threejs,对模型多个动画切换展示

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.AnimationMixer 动画…