并查集基础

abstract

并查集(Union-Find Set)是一种数据结构,主要用于处理动态连通性问题(Dynamic Connectivity Problem),例如在图论中判断两点是否属于同一个连通分量,以及动态地合并集合。

它广泛应用于解决最小生成树(Minimum Spanning Tree)、网络连接问题等领域。

并查集的概念

并查集是一种简单的集合表示,它支持以下3种操作:

  1. Initial(S):将集合S中的每个元素都初始化为只有一个单元素的子集合
  2. Union(S, Root1, Root2):把集合S中的子集合Root2并入子集合Root1。要求Root1和Root2不相交,否则不执行合并。
  3. Find(S, x):找到集合S中单元x所在的子集合,并返回该子集合的根结点

集合和动态连通性

并查集维护的是一些不相交的集合(Disjoint Sets)

例如,有元素集合 {1, 2, 3, 4, 5},可以有如下集合划分:

  • 初始状态:{ {1}, {2}, {3}, {4}, {5} }
  • 合并 {1}{2}{ {1, 2}, {3}, {4}, {5} }
  • 再合并 {3}{4}{ {1, 2}, {3, 4}, {5} }
  • 判断 12 是否属于同一集合:否

并查集的存储结构

通常用树的双亲表示法作为并查集的存储结构,每个子集合是一棵树表示。

所有表示子集合的树,构成一个森林。存放在双亲表示数组

通常使用数组元素的下标代表集合中的元素(名字),用根节点的下标代表子集合名字

树中每个结点的双亲指针指向父节点,根结点的双亲指针为负数(可设置为该集合元素数量的相反数)。

在采用树的双亲指针数组表示作为并查集的存储表示时,集合元素的编号从0到SIZE-1
其中SIZE是最大元素的个数。


初始化示例

集合 S = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } S=\set{0,1,2,3,4,5,6,7,8,9} S={0,1,2,3,4,5,6,7,8,9},初始时每个元素自成一个单元素子集合。

数组表示:

下标0123456789
父节点-1-1-1-1-1-1-1-1-1-1

子集合的合并示例

例如,经过一定计算,子集合并为3个更大的子集合

  • S 1 = { 0 , 6 , 7 , 8 } S_1 = \set{0, 6, 7, 8} S1={0,6,7,8}
  • S 2 = { 1 , 4 , 9 } S_2 = \set{1, 4, 9} S2={1,4,9}
  • S 3 = { 2 , 3 , 5 } S_3 = \set{2, 3, 5} S3={2,3,5}
树状表示:
S1
0
6
7
8
S2
1
4
9
S1
2
3
5

三个子集的名字分别用根节点 0 , 1 , 2 0,1,2 0,1,2表示

例如,在存储结构(双亲表示)中,元素(结点) 6 , 7 , 8 6,7,8 6,7,8的双亲结点为结点 0 0 0;于是在双亲表示数组中,下标为 6 , 7 , 8 6,7,8 6,7,8的数组分量中的值都指示 0 0 0,表明结点 6 , 7 , 8 6,7,8 6,7,8处在结点 0 0 0所表示的子集中(子集名为0),而结点0本身对应的数组分量存放的是子集0中包含的元素数量的相反数(是个负数)

其余两个子集类似

数组表示:
下标0123456789
父节点-4-3-32120001

说明:

  • 负数表示根结点,数值是集合中元素的数量(取反)。
  • 例如下标 0 的值 -4 表示集合 S 1 S_1 S1 包含4个元素。

集合的合并操作

为将两个子集合合并,需将其中一个集合的根结点的双亲指针指向另一个集合的根结点。

例如合并 S 12 = S 1 ∪ S 2 S_{12}=S_1\cup S_2 S12=S1S2 ,结果如下:

树状表示:
S2
S1
0
1
4
6
7
8
9
下标0123456789
父节点-70-32120001

说明:

可以看到,在这类合并操作执行后,双亲表示数组中,如果要找到元素4所在集合 S 12 S_{12} S12的根节点,就不能保证S[4]是所需要的答案(本例中S[4]=1不是 S 12 S_{12} S12的根节点;需要继续向上跟踪,再次访问S[S[4]]=S[1]=0,0还不是负数,所以要再次跟踪,S[0],发现S[0]=-7,由此可知0就是最初元素4所在子集合的根节点

这种改变让查询变得不够直接,可以通过路径压缩操作来改进

并查集的基本实现

并查集的结构定义如下:

#define SIZE 100
int UFSets[SIZE]; //集合元素数组 (双亲指针数组)

下面是并查集主要运算的实现。
(1)并查集的初始化操作(双亲表示数组中对应分量设置为-1,表示初始每个子集仅有一个元素)

void Initial(int S[]) {for(int i=0; i<SIZE; i++)//每个元素自成单元素集合S[i] = -1;
}

(2)并查集的Find操作
在并查集S中查找并返回包含元素x的树的根。

int Find(int S[], int x) {while(S[x] >= 0)//循环寻找x,直到S[x]是负数为止(离开循环时,x是非负的,S[x]是负数)x = S[x];//更新x(这里保证x是非负的)return x;//返回非负的值,表示元素x所在子集的根节点
}

判断两个元素是否属于同一集合,只需分别找到它们的根,再比较根是否相同即可。
(3)并查集的Union操作
求两个不相交子集合的并集。

若将两个元素所在的集合合并为一个集合,则需要先找到两个元素的根,再令一棵子集树的根指向另一棵子集树的根

void Union(int S[], int Root1, int Root2) {if(Root1 == Root2) return;//两个根相同不合并S[Root2] = Root1;//根不同,将用其中一个根Root2的双亲结点指向另一个根Root1
}

小结

Find操作和Union操作的时间复杂度分别为O(d)和O(1),其中d为树的深度。

并查集实现的优化

在极端情况下, n n n个元素构成的集合树的深度为n,则Find操作的最坏时间复杂度为O(n)。

改进的办法是:在做Union操作之前,首先判别子集中的成员数量,然后令成员少的根指向成员多的根,即把小树合并到大树,为此可令根结点的绝对值保存集合树中的成员数量
(1)改进的Union操作

void Union(int S[], int Root1, int Root2) {if(Root1 == Root2) return;//(负数的绝对值越小,值越大abs(S[Root2])<abs(S[Root1],则S[Root2] > S[Root1])if(S[Root2] > S[Root1]) { // Root2 结点数更少)S[Root1] += S[Root2]; // 累加集合树的结点总数S[Root2] = Root1;      // 小树合并到大树} else {S[Root2] += S[Root1]; // 累加结点总数S[Root1] = Root2;     // 小树合并到大树}
}

采用这种方法构造得到的集合树,其深度不超过 log ⁡ 2 n + 1 \log_2 n + 1 log2n+1(这里 n n n为元素数量)

随着子集逐对合并,集合树的深度越来越大,为了进一步减少确定元素所在集合的时间,还可进一步对上述Find操作进行优化,当所查元素 x x x不在树的第二层时,在算法中增加一个“压缩路径”的功能,即将从根到元素x路径上的所有元素都变成根的孩子

例如 r o o t , p 1 , p 2 , . . . , p m , x , . . . {root,p_1,p_2,...,p_m,x,...} root,p1,p2,...,pm,x,...,通过压缩操作,比如依次将 x , p m , ⋯ , p 2 , p 1 {x,p_{m},\cdots,p_{2},p_{1}} x,pm,,p2,p1挂到 r o o t root root结点下;

而挂到root结点下这个操作对于双亲表示法,就是将对应的结点的数组分量(父节点)设置为root;

(2)改进find操作

int Find(int S[], int x) {int root=x;//根节点编号初始化为x//找到x所在子集根节点while(s[root]>=0)root=s[root];//这时候已经找到root了,但是为了之后的新查找更快,做路径压缩while(x!=root){//压缩路径(将树的高于第2层的结点进行压缩)int t=S[x];//t指向x的父节点(以便于处理完x沿着路径往祖先结点继续处理)S[x]=root;//x直接挂到根节点下面x=t;//更新下一个需要处理的目标}return root;//返回根节点编号
}

通过 Find 操作的“压缩路径”优化后,可使集合树的深度不超过 O ( α ( n ) ) O(\alpha(n)) O(α(n)),其中 α ( n ) \alpha(n) α(n) 是一个增长极其缓慢的函数,对于常见的正整数 n n n,通常 α ( n ) ≤ 4 \alpha(n) \leq 4 α(n)4

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

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

相关文章

CloudberryDB(一)安装部署多节点分布式数据库集群

CloudberryDB&#xff1a; 一个 Greenplum Database 分布式数据库开源版本的衍生项目&#xff0c; 针对开源 Greenplum Database 优化的地方&#xff0c; CloudberryDB制定了路线图&#xff08;https://github.com/orgs/cloudberrydb/discussions/369&#xff09;并在逐步改…

解决Logitech G hub 无法进入一直转圈的方案(2024.12)

如果你不是最新版本无法加载尝试以下方案&#xff1a;删除AppData 文件夹下的logihub文件夹 具体路径&#xff1a;用户名根据实际你的请情况修改 C:\Users\Administrator\AppData\Local 如果你有通过lua编译脚本&#xff0c;记得备份&#xff01;&#xff01; ↓如果你是最新…

数据库范式与反范式化:如何权衡性能与数据一致性

目录 1. 什么是数据库范式&#xff08;Normalization&#xff09;&#xff1f;第一范式&#xff08;1NF&#xff09;第二范式&#xff08;2NF&#xff09;第三范式&#xff08;3NF&#xff09; 2. 什么是反范式化&#xff08;Denormalization&#xff09;&#xff1f;3. 反范式…

Nmap使用总结

0X00 背景 nmap是测试中常用的网络探测工具&#xff0c;但是这回简单的操作&#xff0c;一直了解不深入&#xff0c;现在深入的了解和学习一下。 在文章结构上&#xff0c;我把平时常用的内容提前了&#xff0c;以便再次查阅的时候&#xff0c;比较方便。 0X01 安装 nmap可…

【记录49】vue2 vue-office在线预览 docx、pdf、excel文档

vue2 在线预览 docx、pdf、excel文档 docx npm install vue-office/docx vue-demi0.14.6 指定版本 npm install vue-office/docx vue-demi <template><VueOfficeDocx :src"pdf" style"height: 100vh;" rendere"rendereHandler" error&…

MVC模式的理解和实践

在软件开发中&#xff0c;MVC&#xff08;Model-View-Controller&#xff09;模式是一种经典的设计模式&#xff0c;特别适用于构建用户界面复杂的Web应用程序。MVC通过将应用程序的业务逻辑、数据显示和用户交互分离&#xff0c;使代码结构更加清晰&#xff0c;易于维护和扩展…

[A-22]ARMv8/v9-SMMU多级页表架构

ver0.1 [看前序文章有惊喜,关注W\X\G=Z+H=“浩瀚架构师”,可以解锁全部文章] 前言 前文我们对SMMU的系统架构和基本功能做了简要的介绍,现在大家大致对SMMU在基于ARM体系的系统架构下的总线位置和产品形态有了基本的了解。这里我们还是简单做个前情回顾,从总线架构角度看…

【UE5 “RuntimeLoadFbx”插件】运行时加载FBX模型

前言 为了解决在Runtime时能够直接根据FBX模型路径直接加载FBX的问题&#xff0c;推荐一款名为“RuntimeLoadFBX”的插件。 用法 插件用法如下&#xff0c;只需要指定fbx的地址就可以在场景中生成Actor模型 通过指定输入参数“Cal Collision”来设置FBX模型的碰撞 还可以通过…

(11)(3.1) ESC接地和接线注意事项

文章目录 前言 1 归纳 2 电容式 3 电阻 前言 ESC 接地问题由 3 种形式的 ESC 信号/耦合问题组成&#xff0c;即电阻、电容和电感。在制造飞机时&#xff0c;应考虑这三个因素。 1 归纳 这是电流突然变化导致系统中出现大电压尖峰的趋势。电源系统中的电感主要是由 ESC 和…

精品基于Python实现的微信小程序校园导航系统-微信小程序

[含文档PPT源码等] [包运行成功永久免费答疑辅导] 《django微信小程序校园导航系统》该项目采用技术Python的django框架、mysql数据库 &#xff0c;项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、核心代码介绍视频等 软件开发环境及开发工具&#xf…

Rstudio-server的安装、配置、维护

一、安装Rstudio-server (1)安装R语言&#xff1a; sudo apt install r-base # 如果没有管理员权限无法操作 # 这样装上R默认在/usr/bin/R其实基本上的流程都可以参考posit的官网&#xff08;也就是Rstudio的官网&#xff09;&#xff1a; https://posit.co/download/rstudio…

Python序列的应用(八):元组、字典

前言&#xff1a;在Python编程语言中&#xff0c;序列是一种非常重要的数据结构&#xff0c;它允许我们存储和操作有序的数据集合。在前几期的内容中&#xff0c;我们已经探讨了列表&#xff08;List&#xff09;和集合&#xff08;Set&#xff09;这两种序列的应用&#xff0c…

OpenCV 功能函数介绍

一&#xff0c; 二值化函数 功能&#xff1a; 用于对图像进行二值化处理 参数&#xff1a; cv2.threshold(输入你的图像所对应的灰度图&#xff0c; 阈值&#xff1a;是浮点还是整数取决予图像的数据类型 最大值;高于阈值的像素值&#xff0c; 阈值类型&#xff1a;cv2.THR…

【Python】使用Selenium的find_element模块获取网页上的大段文字和表格的方法(建议收藏!)

发现了一个使用Selenium的find_element模块&#xff0c;快速获取文字和表格的方法&#xff0c;很实在&#xff0c;以后爬网的时候&#xff0c;就不用beautifulSoup 和 pandas的read_html 混起来用了&#xff01; 文字部分&#xff1a;实现网络节点下&#xff0c;某个节点下的其…

APP渗透测试记录(一、Android应用基本构造)

Android应用基本构造 雷电模拟机进入 adb shell# 如果不是root权限 su一下 su 1.了解APK文件 安卓应用的扩展名为.apk(Android Application Package),它是一个包含多个文件和文件夹的数据存档文件。 1.1 apk文件解压后的目录结构 AndroidManifest.xml:包含应用的大部分…

【AI知识】有监督学习之回归任务(附线性回归代码及可视化)

1. 回归的基本概念 在机器学习的有监督学习中&#xff0c;回归&#xff08;Regression&#xff09;是一种常见的任务&#xff0c;它的目标是通过观察数据来建立一个模型&#xff0c;用一个或多个自变量来预测因变量的值。 回归分析通常用于&#xff1a; a.预测&#xff0c;基于…

fastadmin批量压缩下载远程视频文件

后端代码 // 批量下载并压缩 public function downloadAll(){$ids input(ids);$row $this->model->where(id, in, $ids)->field(id,title,video_url)->select();if (!$row) {$this->error(记录不存在);}$arr [];$tempFiles []; // 用来存储临时下载的视频文…

边缘计算+人工智能:让设备更聪明的秘密

引言&#xff1a;日常生活中的“智能”设备 你是否发现&#xff0c;身边的设备正变得越来越“聪明”&#xff1f; 早上醒来时&#xff0c;智能音箱已经根据你的日程播放舒缓音乐&#xff1b;走进厨房&#xff0c;智能冰箱提醒你今天的食材库存&#xff1b;而在城市道路上&…

JVM 双亲委派模型以及垃圾回收机制

目录 1. JVM 内存区域划分 2. JVM 中类加载的过程 1) 类加载的基本流程 2) 双亲委派模型 3. JVM 中垃圾回收机制 1) 找到垃圾 a) 引用计数 b) 可达性分析 2) 释放垃圾 1. JVM 内存区域划分 一个运行起来的 Java 进程&#xff0c;其实就是一个 JVM 虚拟机。 而进程是…

ansible自动化运维(四)jinjia2模板

Jinjia2模板 前面说到playbook组成的时候&#xff0c;有介绍到template模块&#xff0c;而template模块对模板文件进行渲染时&#xff0c;使用的就是jinja2模板引擎&#xff0c;jinja2本身就是基于python的模板引擎&#xff0c;所以下面先来了解一下jinjia2模板的一些用法 基…