当前位置: 首页 > news >正文

并查集算法篇上期:并查集原理及实现

在这里插入图片描述

引入

那么我们在介绍我们并查集的原理之前,我们先来看一下并查集所应用的一个场景:那么现在我们有一个长度为n的数组,他们分别属于不同的集合,那么现在我们要查询数组当中某个元素和其他元素是否处于同一集合当中,或者我们想把它们合并到同一个集合当中,以及查询该集合的数量,那么这些都可以交给我们的并查集来进行实现。

那么我们数组中处于相同的集合的元素的位置是离散的,那么很多人看到并查集所应用的场景,那么常规思路实现我们的并查集一般会想到就是采取我们的哈希表这个数据结构,对于每一个集合我们建立一张哈希表,如果我们查询这两个元素是否在同一个集合当中,那么我们就是确定他们是否在同一张表当中,那么就会遍历我们这张哈希表,如果要合并两个不同的集合,那么我们就该另一张哈希表中的元素全部添加进该哈希表当中,我们哈希表实现我们的并查集是肯定没有问题的,但是我们有更为简单并且高效的实现方式,那么就是通过我们的数组来实现


并查集原理

那么我们首先我们知道我们判断两个元素所处的集合是否是一个集合,那么我们的集合肯定得有一个标识符来进行区分,那么这里我们区分不同的集合的话,我们就是选取该集合当中的一个代表元素的索引或者编号来作为该集合的一个标识符。

那么我们会首先准备一个father数组和一个size数组(其中size数组可有可不有),那么我们的father数组的每一个位置就对应我们原始数组当中的每一个位置,那么其中father数组的作用就是确定数组每一个位置在集合当中的直接后继的节点编号是谁,那么想你可能看不懂我刚才的那句话,但是没关系,我在下文会进行讲解。
那么我们首先初始化我们的并查集的时候,我们将我们该数组中的每个元素自己作为一个集合,那么该集合的代表元素就是他们本身

那么我们如何理解我们这个集合呢,那么我们对于每一个集合来说,那么处于该集合当中的元素就是以一个树的形式来进行组织的,那么我们该树的根节点就是该集合的代表元素,这个树不是说我们的并查集的集合的实现就是按照真的数据结构当中的树那样用指针进行构造,而是说我们的处于同一集合当中的元素可以形象理解为他们是以一个树的形式来组织,就像我们理解DFS的递归过程就形象的理解为类似于一棵多叉树的遍历。

那么最开始我们初始化并查集的时候,假设我们现在有一个长度为n的数组,那么我们先让该数组当中的每一个元素自己作为一个集合,那么我们就可以理解为每个集合当中所对应对应的树的节点就只有一个就是当前数组每个元素它本身,那么它的下一个直接后继节点就是它自己。

那么我们该数组中任意两个位置所处的集合要进行合并的话,那么我们就首先判断他们是否处于同一个集合,如果处于同一个集合,就没有必要进行合并,如果不处于同一个集合的话,那么我们就可以合并,那么刚开始我们每一个集合都只有当前数组每一个位置本身的一个元素,那么我们合并的话,我们就将我们这个其中一个集合所对应的树的根结点原本就是自己本身,并且它的指向后继节点的指针是指向自己,那么我们就该根结点的指针给指向另一个元素所处集合所对应树的根结点,那么我们实现这个过程的话,我们知道我们father数组记录的是数组当中每一个位置它的父节点的索引编号,那么最开始由于我们每一个元素自己作为一个集合,那么father数组当中的每一个位置的记录的父节点的编号就是他们本身
例:father[0]=0,father[1]=1,father[2]=2…

那么如果此时我们假设数组下标为0的元素与数组下标为1的元素合并了,那么我们就在father数组当中下标为0的位置处原本记录的编号就是0,那么我们将其修改为1,那么此时这步操作我们就可以理解为我们一个集合对应的树的根节点的指针原本的指向是自己,而现在我们在该集合所对应的树插入到另一个集合所对应的树当中作为一个子树了,而我们该集合的根节点原本的指向是自己,现在我们将其修改指向为另一个要合并集合对应的树的根节点,所以此时该树的根节点不再是原先集合的根节点了,而是另一个我们插入的集合原本的根节点,所以现在两个集合的根节点相同,那么两个集合就是同一个集合了,从而实现合并。

那么接下来我们每次合并两个数组中任意两个数所处的集合,我们都要先查找两个数所处的集合的代表元素的编号,看他们是否相同,而现在假设这个数组中的每个集合已经经过多次的合并,那么意味着该元素所处的树上的节点可能不只有之前初始化时它自己一个了,那么我们就得往上找到该元素所处的树的根结点,那么我们知道father数组记录的是数组中每一个位置的元素在树当中的直接后继,也就是它的父节点,那么接下来我们就需要遍历我们的father数组,假设我们现在要找下标为0的元素所处集合的根结点也就是代表元素,那么我们就遍历father数组,那么我们先找到father数组下标为0的元素值,也就是下标为0的节点在树中的直接后继节点也就是父节点的编号,假设为1,那么接下来我们就到father数组下标为1位置处查看它所记录的编号为1的直接后继节点的编号是谁,然后再对应跳转到该father数组的编号位置处,而我们的根结点的直接后继我们在初始化设置的时候就是它自己,所以如果我们发现father数组下标为3的记录的直接后继编号就是3,那么3就是当前的下标为0的元素所处集合的代表元素。
在这里插入图片描述


那么我们知道了我们查询以及合并的一个原理之后,那么我们就可以写我们并查集最为关键的两个函数:find函数和union函数,那么在给处这两个板子之前,我们还能对并查集的进行两个优化

小挂大

那么我们除了我们的father数组,那么我们还可以有我们的size数组,那么size数组的每个位置和father数组一样对应原数数组当中的每一个位置,我们size数组的作用则是记录我们每一个集合的元素个数,那么我们要查找数组下标i位置所处的集合的元素个数,那么我们就需要调用find函数找到我们下标为i位置的代表元素的下标q,那么我们查询size[q],就可以查到i所处集合中的元素了,那么所谓的小挂大,就是我们知道我们合并两个不同的集合,我们是将其中的一个集合所对应的树给插入到另一个集合对应的树中作为子树,让该集合的树的根节点的指向修改指向另一个树的根节点来达到

但是我们对于并查集当中的操作,真正影响时间复杂度的其实是我们的find操作,因为我们每一次union操作前都要先find来确定完两个集合不相同后,那么我们只需要将该集合根节点所对应的father数组修改为另一个集合的father数组的值,而数组由于随机访问,那么这步修改代价的时间复杂度是o(1),而我们find从当前下标为i的元素在树中往上遍历访问到根节点的时间复杂度则是o(n),所以我们优化时间性能就是尽可能让元素少的集合去插入到元素大的集合中去,那么这样往上遍历的节点个数就相比于大挂小的节点个数要小,所以遍历代价就会减小,这就是我们的小挂大的优化
在这里插入图片描述

路径压缩

而路径压缩的方式是我们这两个优化中最高效的,那么我们掌握了路径压缩,我们甚至都不需要来小挂大来额外建立一个size数组,但是为了让我们对并查集的理解更全面,我还是介绍了小挂大的策略

那么我们的路径压缩就是我们当我们执行find操作的时候,去查询该位置所处集合当中的根节点的时候,我们会沿途往上遍历直到达到根节点,那么我们这里在沿途往上遍历的过程中,我们将我们沿途的每一个节点直接修改连接到根节点,那么这样我们每一次find的时候,我们该节点往上遍历就直接是根节点从而直接得到代表元素,那么每次查询的时间复杂度就可以优化到O(1)!但是我们路径压缩的过程会有一个o(N)的代价,但是一旦压缩之后,之后的find都是常数时间复杂度了,那么这个路径压缩优化下并查集的具体时间复杂度是专门有数学学家花了几十年时间来证明,那么感兴趣的话,可以下去自己去了解,那么这里我就不在赘述了
在这里插入图片描述

而具体我们怎么将我们的沿途的各个节点直接连接根节点,那么我们就通过栈或者递归来实现,其中递归的实现原理就是我们的从当前该节点先递归找到根节点,然后回溯到我们当前节点时,会依次返回我们根节点的节点编号然后从而修改沿途节点的father数组的值。

find函数递归版本代码板子:

int find(vector<int>& father,int x)
{if(x!=father[x]){father[x]=find(father[x]);}return father[x];
}

union函数代码板子:

void _union(vector<int>& father,int x,int y)
{int fx=find(father,x);int fy=find(father,y);if(fx!=fy){father[fx]=fy;}return;
}

初始化father数组:

vector<int> father(nums.size()); // 创建一个与nums数组大小相同的father数组
for (int i = 0; i < father.size(); i++) {father[i] = i; // 将father数组的每个元素初始化为它自己的索引
}

结语

那么这就是本篇并查集的全部内容,本篇文章就介绍了并查集的原理以及实现,那么相比于之前我的算法文章,我还会引入几个与该算法相关的题目来应用,但是由于博主最近有点忙,所以就打算将我们的并查集算法篇分为两期,一期讲原理另一期讲题,所以这篇文章相比于我们之前的文章来说字数就较少,那么我下一期我将会讲并查集的相关题目,我会持续更新,希望你多多关注,那么如果本篇文章有帮助到你的话,还请多多三连关注支持一下博主哦,你的支持就是我最大的动力!
在这里插入图片描述

相关文章:

并查集算法篇上期:并查集原理及实现

引入 那么我们在介绍我们并查集的原理之前&#xff0c;我们先来看一下并查集所应用的一个场景&#xff1a;那么现在我们有一个长度为n的数组&#xff0c;他们分别属于不同的集合&#xff0c;那么现在我们要查询数组当中某个元素和其他元素是否处于同一集合当中&#xff0c;或者…...

如何在WPS打开的word、excel文件中,使用AI?

1、百度搜索&#xff1a;Office AI官方下载 或者直接打开网址&#xff1a;https://www.office-ai.cn/static/introductions/officeai/smartdownload.html 打开后会直接提示开始下载中&#xff0c;下载完成后会让其选择下载存放位置&#xff1a; 选择位置&#xff0c;然后命名文…...

【Deepseek+Dify】wsl2+docker+Deepseek+Dify部署本地大模型知识库问题总结

wsl2dockerDeepseekDify部署本地大模型知识库问题总结 基于ollama部署本地文本模型和嵌入模型 部署教程 DeepSeekdify 本地知识库&#xff1a;真的太香了 问题贴&#xff1a;启动wsl中docker中的dify相关的容器 发现postgre服务和daemon服务一直在重启&#xff0c;导致前端加…...

C++初阶——简单实现vector

目录 1、前言 2、Vector.h 3、Test.cpp 1、前言 简单实现std::vector类模板。 相较于前面的string&#xff0c;vector要注意&#xff1a; 深拷贝&#xff0c;因为vector的元素可能是类类型&#xff0c;类类型元素可以通过赋值重载&#xff0c;自己实现深拷贝。 迭代器失效…...

1.21作业

1 unserialize3 当序列化字符串中属性个数大于实际属性个数时&#xff0c;不会执行反序列化 外部如果是unserialize&#xff08;&#xff09;会调用wakeup&#xff08;&#xff09;方法&#xff0c;输出“bad request”——构造url绕过wakeup 类型&#xff1a;public class&…...

深度集成DeepSeek大模型:WebSocket流式聊天实现

目录 5分钟快速接入DeepSeek大模型&#xff1a;WebSocket实时聊天指南创建应用开发后端代码 (Python/Node.js)结语 5分钟快速接入DeepSeek大模型&#xff1a;WebSocket实时聊天指南 创建应用 访问DeepSeek官网 前往 DeepSeek官网。如果还没有账号&#xff0c;需要先注册一个。…...

Jmeter连接数据库、逻辑控制器、定时器

Jmeter直连数据库 直接数据库的使用场景 直连数据库的关键配置 添加MYSQL驱动Jar包 方式一&#xff1a;在测试计划面板点击“浏览”按钮&#xff0c;将你的JDBC驱动添加进来 方式二&#xff1a;将MySQL驱动jar包放入到lib/ext目录下&#xff0c;重启JMeter 配置数据库连接信…...

『Linux笔记』进程间通信(IPC)详细介绍!

进程间通信&#xff08;IPC&#xff09;详细介绍&#xff01; 文章目录 一. 进程间通信&#xff08;IPC&#xff09;详细介绍1. 共享内存&#xff08;Shared Memory&#xff09;2. 消息队列&#xff08;Message Queues&#xff09;3. 信号量&#xff08;Semaphores&#xff09…...

Jmeter进阶篇(34)如何解决jmeter.save.saveservice.timestamp_format=ms报错?

问题描述 今天使用Jmeter完成压测执行,然后使用命令将jtl文件转换成html报告时,遇到了报错! 大致就是说jmeter里定义了一个jmeter.save.saveservice.timestamp_format=ms的时间格式,但是jtl文件中的时间格式不是标准的这个ms格式,导致无法正常解析。对于这个问题,有如下…...

Visual Studio 2022配置网址参考

代码格式化和清理冗余代码选项的配置&#xff1a; 代码样式选项和代码清理 - Visual Studio (Windows) | Microsoft Learn 调试时传递参数&#xff1a; 调试时传递命令行参数&#xff08;C&#xff09; - Visual Studio (Windows) | Microsoft Learn...

Redis中集合(Set)常见命令详解

集合&#xff08;Set&#xff09;常见命令详解 集合&#xff08;Set&#xff09;在Redis中是一种无序且不可重复的数据结构&#xff0c;非常适合用于存储唯一元素的集合。以下是Redis集合操作的一些常用命令及其详细说明&#xff1a; 添加成员 sadd key member [member ...]…...

动态规划

简介 动态规划最核心两步&#xff1a; 状态表示&#xff1a;dp[i]代表什么状态转移方程&#xff1a;如何利用已有的dp求解dp[i] 只要这两步搞对了&#xff0c; 就完成了动态规划的%95 剩下的就是细节问题&#xff1a; dp初始化顺序&#xff08;有时是倒序&#xff09;处理边…...

stm32rtc实时时钟详解文章

目录 stm32 后备区域基础知识详解 stm32 bkp基础知识详解 Unix时间戳基础知识详解 stm32 rtc实时时钟基础知识详解 相关代码初始化配置 欢迎指正&#xff0c;希望对你&#xff0c;有所帮助&#xff01;&#xff01;&#xff01; stm32 后备区域基础知识详解 stm32芯片的 …...

DeepSeek 助力 Vue 开发:打造丝滑的 键盘快捷键(Keyboard Shortcuts)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…...

【第一节】C++设计模式(创建型模式)-工厂模式

目录 前言 一、面向对象的两类对象创建问题 二、解决问题 三、工厂模式代码示例 四、工厂模式的核心功能 五、工厂模式的应用场景 六、工厂模式的实现与结构 七、工厂模式的优缺点 八、工厂模式的扩展与优化 九、总结 前言 在面向对象系统设计中&#xff0c;开发者常…...

深入理解 SQL 注入漏洞及解决方案

一、引言 在当今数字化时代&#xff0c;数据库作为存储和管理数据的核心组件&#xff0c;其安全性至关重要。SQL 注入是一种常见且极具威胁性的数据库安全漏洞&#xff0c;它可能导致数据泄露、篡改甚至系统被完全控制。本文将深入探讨 SQL 注入漏洞的产生原因、表现形式以及如…...

使用 deepseek实现 go语言,读取文本文件的功能,要求支持 ascii,utf-8 等多种格式自适应

使用 deepseek实现 go语言&#xff0c;读取文本文件的功能&#xff0c;要求支持 ascii&#xff0c;utf-8 等多种格式自适应我要用 chatgpt&#xff0c;也问过&#xff0c;但是比 deepseek 还是差一个级别&#xff0c;具体如下&#xff1a; package mainimport ("bufio&qu…...

7.【线性代数】——求解Ax=0,主列和自由列

七 求解Ax0&#xff0c;主列和自由列 1. 消元、秩、特解特解零空间 2. 简化行阶梯形式 :主元上下都是0&#xff0c;主元简化为1 1. 消元、秩、特解 矩阵消元 [ 1 2 2 2 2 4 6 8 3 6 8 10 ] ⏟ A ⇒ r o w 2 − 2 r o w 1 , r o w 3 − 3 r o w 1 [ 1 2 2 2 0 0 2 4 0 0 2 4 ]…...

vue3结合后端传递过来的文件进行预览功能

业务的需要&#xff0c;前端需要根据后端传递过来的文件流进行预览的功能&#xff0c;前端点击链接直接触发浏览器的窗口的预览功能。 实现方式一&#xff1a; 使用弹窗和iframe的标签的形式进行预览文件&#xff0c;但是iframe可能会出现网站安全性的问题&#xff0c;限制比较…...

【Python爬虫(39)】掌控全局:分布式爬虫的任务管理与监控之道

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

论文阅读:Matting by Generation

今天介绍一篇关于 matting 抠图的文章&#xff0c;抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法&#xff0c;已经有很多的工作和这个任务相关。这两年 diffusion 模型很火&#xff0c;大家又开始用 diffusion 模型做各种 CV 任务了&am…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要&#xff1a;在消费市场竞争日益激烈的当下&#xff0c;传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序&#xff0c;探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式&#xff0c;分析沉浸式体验的优势与价值…...