数据结构---并查集

目录

一、并查集的概念        

二、并查集的实现

三、并查集的应用


一、并查集的概念        

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

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

虽然这张图画出来是树的形状,但是实际上我们并查集用的数组来存储的。

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

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

现在0集合有7个人,2集合有3个人,总共两个朋友圈。
通过以上例子可知,并查集一般可以解决一下问题:
(1)查找元素属于哪个集合
        沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)
(2)查看两个元素是否属于同一个集合
        沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
(3)将两个集合归并成一个集合
        将两个集合中的元素合并
        将一个集合名称改成另一个集合的名称
(4)集合的个数
        遍历数组,数组中元素为负数的个数即为集合的个数。

        并查集其实就是一个帮助我们判断多个元素是否在一个分组的数据结构,他并没有像红黑树这种数据结构的查找、排序功能。

二、并查集的实现

//这个并查集里面啥数据都不存,就存父亲下标或者树的孩子数量
//该数组有几个元素,说明该并查集有几个元素
class UnionFindSet
{
public:
	UnionFindSet(size_t n)
		:_ufs(n,-1)
	{

	}
	//两棵树进行合并
	void Union(int x1,int x2)
	{
		//找到他的根,判断两个值本身是否在一个集合中
		int root1=FindRoot(x1);
		int root2 = FindRoot(x2);
		if (root1==root2)
		{
			return;
		}
		else
		{
			//数据量小的往数据量大的合并
			//root1大,root2小
			if (abs(_ufs[root1])<abs(_ufs[root2]))
			{
				swap(root1,root2);
			}
			//第一棵树数量加上第二棵树的数量
			_ufs[root1] += _ufs[root2];
			//第二棵树存第一棵树的根节点下标
			_ufs[root2] = root1;
		}
	}

	//查找一个值的根(这里的x是下标的意思)
	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 isInSet(int x1,int x2)
	{
		return FindRoot(x1) == FindRoot(x2);
	}

	//一共有几棵树
	size_t SetSize()
	{
		size_t size = 0;
		for (size_t i=0;i<_ufs.size();i++)
		{
			if (_ufs[i]<0)
			{
				size++;
			}
		}
		return size;
	}


private:
	vector<int> _ufs;
};

三、并查集的应用

        这就是一道并查集的典型应用题,矩阵就是城市数组竖着横着排列组合而成的,如果i,j位置为1说明两个城市连通,那么我们可以将连通的城市放到一个集合中,不连通的城市单独放到一个集合,最后计算该并查集中有几个集合就知道有几个省份了。

class Solution {
public:
    //这里的二维数组,意思是:一维数组都是一个城市,每一个城市下面还存储了一个数组,
    //该数组用来表示和其他城市的连接状态
    int findCircleNum(vector<vector<int>>& isConnected) {
        //用城市的总个数来构造一个并查集,初始时都是-1各自独立
        UnionFindSet ufs(isConnected.size());
        
        //遍历每一个城市
        for(size_t i=0;i<isConnected.size();i++)
        {
            //在每一个城市中,找到自己的连通信息,如果自己信息数组【j】为1,则说明自己城市和j城市连通,则放入一个树中
            for(size_t j=0;j<isConnected[i].size();j++)
            {
                //如果两个城市直接相互连同,则放到一棵树中
                if(isConnected[i][j]==1)
                {
                    ufs.Union(i,j);
                }
            }
        }
        return ufs.SetSize();
    }
};

        因为题目中限制了字母只会在a-z中出现,而英文字母天然在ascii中就是有序存放的,所以我们知道他们的一一映射关系,即可创建一个26大小的并查集,其中[0]对应a,[25]对应z。

        在使用字母找到映射的下标的时候,只需要用该字母的ascii码-'a'即可对应上0-25下标。

class Solution {
public:
    bool equationsPossible(vector<string>& equations) {
        //因为题目说明字母只有小写的a-z,而他们在ascii中又天然有序,所以创建一个26大小的并查集
        UnionFindSet ufs(26);
        //第一次遍历vector<string>将题目中说相等的放到一个集合
        for(auto& str:equations)
        {
            if(str[1]=='=')
            {
                //如果本身不在一个集合,则合并到一个集合中
                int root1=ufs.FindRoot(str[0]-'a');
                int root2=ufs.FindRoot(str[3]-'a');
                if(root1!=root2)
                {
                    ufs.Union(str[0]-'a',str[3]-'a');
                }
            }
        }

        //第二次遍历,如果发现不相等之前被我们放到一个集合,则错误。全部没有则正确
        for(auto& str:equations)
        {
            if(str[1]=='!')
            {
                int root1=ufs.FindRoot(str[0]-'a');
                int root2=ufs.FindRoot(str[3]-'a');
                if(root1==root2)
                {
                    return false;
                }
            }
        }
        return true;
    }
};

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

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

相关文章

STM32 FreeRTOS内存管理简介

在使用 FreeRTOS 创建任务、队列、信号量等对象时&#xff0c;通常都有动态创建和静态创建的方式。动态方式提供了更灵活的内存管理&#xff0c;而静态方式则更注重内存的静态分配和控制。 如果是1的&#xff0c;那么标准 C 库 malloc() 和 free() 函数有时可用于此目的&#…

构建core模块

文章目录 1.环境搭建1.sunrays-common下新建core模块2.引入依赖&#xff0c;并设置打包常规配置 2.测试使用1.启动&#xff01;1.创建模块2.引入依赖3.application.yml 配置MySQL和Minio4.创建启动类5.启动测试 2.common-web-starter1.目录2.WebController.java3.结果 3.common…

【Flink系列】6. Flink中的时间和窗口

6. Flink中的时间和窗口 在批处理统计中&#xff0c;我们可以等待一批数据都到齐后&#xff0c;统一处理。但是在实时处理统计中&#xff0c;我们是来一条就得处理一条&#xff0c;那么我们怎么统计最近一段时间内的数据呢&#xff1f;引入“窗口”。 所谓的“窗口”&#xff…

AIGC与劳动力市场:技术进步与就业结构的重塑

随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;尤其是生成式AI&#xff08;AIGC&#xff09;&#xff0c;劳动力市场正经历前所未有的变革。从内容创作到自动化生产线&#xff0c;几乎每个行业都在经历一场技术的洗礼。然而&#xff0c;这场革命并不是全然…

废品回收小程序,数字化回收时代

随着科技的不断创新发展&#xff0c;废品回收在各种技术的支持下也在不断地创新&#xff0c;提高了市场的发展速度&#xff0c;不仅能够让回收效率更加高效&#xff0c;还能够让居民更加便捷地进行回收&#xff0c;推动废品回收行业的发展。 回收市场机遇 目前&#xff0c;废…

题解 CodeForces 430B Balls Game 栈 C/C++

题目传送门&#xff1a; Problem - B - Codeforceshttps://mirror.codeforces.com/contest/430/problem/B翻译&#xff1a; Iahub正在为国际信息学奥林匹克竞赛&#xff08;IOI&#xff09;做准备。有什么比玩一个类似祖玛的游戏更好的训练方法呢&#xff1f; 一排中有n个球…

【Linux】线程全解:概念、操作、互斥与同步机制、线程池实现

&#x1f3ac; 个人主页&#xff1a;谁在夜里看海. &#x1f4d6; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 道阻且长&#xff0c;行则将至 目录 &#x1f4da;一、线程概念 &#x1f4d6; 回顾进程 &#x1f4d6; 引入线程 &#x1f4d6; 总结 &a…

PDF文件提取开源工具调研总结

概述 PDF是一种日常工作中广泛使用的跨平台文档格式&#xff0c;常常包含丰富的内容&#xff1a;包括文本、图表、表格、公式、图像。在现代信息处理工作流中发挥了重要的作用&#xff0c;尤其是RAG项目中&#xff0c;通过将非结构化数据转化为结构化和可访问的信息&#xff0…

简历_使用优化的Redis自增ID策略生成分布式环境下全局唯一ID,用于用户上传数据的命名以及多种ID的生成

系列博客目录 文章目录 系列博客目录WhyRedis自增ID策略 Why 我们需要设置全局唯一ID。原因&#xff1a;当用户抢购时&#xff0c;就会生成订单并保存到tb_voucher_order这张表中&#xff0c;而订单表如果使用数据库自增ID就存在一些问题。 问题&#xff1a;id的规律性太明显、…

跨境电商使用云手机用来做什么呢?

随着跨境电商的发展&#xff0c;越来越多的卖家开始尝试使用云手机来协助他们的业务&#xff0c;这是因为云手机具有许多优势。那么&#xff0c;具体来说&#xff0c;跨境电商使用云手机可以做哪些事情呢&#xff1f; &#xff08;一&#xff09;实现多账号登录和管理 跨境电商…

计算机网络 (47)应用进程跨越网络的通信

前言 计算机网络应用进程跨越网络的通信是一个复杂而关键的过程&#xff0c;它涉及多个层面和组件的协同工作。 一、通信概述 计算机网络中的通信&#xff0c;本质上是不同主机中的应用进程之间的数据交换。为了实现这种通信&#xff0c;需要借助网络协议栈中的各层协议&#x…

Open3D 计算每个点的协方差矩阵【2025最新版】

目录 一、算法原理1、计算公式2、主要函数3、函数源码二、代码实现三、结果展示博客长期更新,本文最近更新时间为:2025年1月18日。 一、算法原理 1、计算公式 对于点云数据中的任意一点 p p p,根据其邻域内点的坐标计算其协方差矩阵。计算公式如下:

e2studio开发RA0E1(16)----配置RTC时钟及显示时间

e2studio开发RA0E1.16--配置RTC时钟及显示时间 概述视频教学样品申请完整代码下载硬件准备参考程序新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_UARTA_Open()函数原型回调函数user_uart_callba…

Go语言strings包与字符串操作:从基础到高级的全面解析

Go语言strings包与字符串操作:从基础到高级的全面解析 引言 Go语言以其简洁、高效和强大的标准库而闻名,其中strings包是处理字符串操作的核心工具。本文将深入探讨Go语言中strings包的功能及其在实际开发中的应用,帮助开发者更好地理解和使用这一工具。 1. strings包概述…

微服务学习-快速搭建

1. 速通版 1.1. git clone 拉取项目代码&#xff0c;导入 idea 中 git clone icoolkj-microservices-code: 致力于搭建微服务架构平台 1.2. git checkout v1.0.1版本 链接地址&#xff1a;icoolkj-microservices-code 标签 - Gitee.com 2. 项目服务结构 3. 实现重点步骤 …

加密货币的基本交易技术指标

是币安交易市场的基本版视图,trading View是有更复杂的参数追踪。币安的交易的技术指标有主图和副图。有很多指标&#xff0c;让ai解释一下相关概念和意义。加密货币交易中可能遇到的主图指标及其含义&#xff1a; 1. MA&#xff08;移动平均线&#xff0c;Moving Average&…

简单介绍JSONStream的使用

地址 作用 这个模块是根据需要筛选出json数据中自己所需要的数据 使用 var JSONStream require("JSONStream"); var parse require("fast-json-parse"); var fs require("fs");fs.createReadStream("./time.json").pipe(JSONSt…

UOS扩容攻略:迁移home

原文链接&#xff1a;UOS扩容攻略&#xff1a;迁移/home Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于 UOS 扩容攻略&#xff1a;迁移 /home 目录 的文章。相信很多朋友在使用 UOS 系统时&#xff0c;会遇到系统分区空间不足&#xff0c;尤其是 /home 目录存…

RK3588平台开发系列讲解(NPU篇)NPU 驱动的组成

文章目录 一、NPU 驱动组成二、查询 NPU 驱动版本三、查询 rknn_server 版本四、查询 librknn_runtime 版本沉淀、分享、成长,让自己和他人都能有所收获!😄 一、NPU 驱动组成 NPU 驱动版本、rknn_server 版本、librknn_runtime 版本以及 RKNN Toolkit 版本的对应关系尤为重…

【实践】操作系统智能助手OS Copilot新功能测评

一、引言 数字化加速发展&#xff0c;尤其人工智能的发展速度越来越快。操作系统智能助手成为提升用户体验与操作效率的关键因素。OS Copilot借助语言模型&#xff0c;人工智能等&#xff0c;对操作系统的自然语言交互操作 推出很多功能&#xff0c;值得开发&#xff0c;尤其运…