一分钟说清楚并查集

简介: 思路比结论重要,有收获就是好的。

分离集合(disjoint set)是一种经典的数据结构,它有三类操作:

Make-set(a):生成包含一个元素a的集合S;

Union(X, Y):合并两个集合X和Y;

Find-set(a):查找元素a所在集合S,即通过元素找集合句柄;

它非常适合用来解决集合合并与查找的问题,也常称为并查集。

一、并查集的链表实现

image.png

如上图,并查集可以用链表来实现。

链表实现的并查集,Find-set(a)的时间复杂度是多少?

集合里的每个元素,都指向“集合的句柄”,这样可以使得“查找元素a所在集合S”,即Find-set(a)操作在O(1)的时间内完成。

链表实现的并查集,Union(X, Y)的时间复杂度是多少?

假设有集合:

S1={7,3,1,4}

S2={1,6}

合并S1和S2两个集合,需要做两件事情:

image.png

(1) 第一个集合的尾元素,链向第二个集合的头元素(蓝线1);

(2) 第二个集合的所有元素,指向第一个集合的句柄(蓝线2,3);

合并完的效果是:
image.png

变成了一个更大的集合S1。

集合合并时,将短的链表,往长的链表上接,这样变动的元素更少,这个优化叫做“加权合并”。

画外音:实现的过程中,集合句柄要存储元素个数,头元素,尾元素等属性,以方便上述操作进行。

假设每个集合的平均元素个数是n,Union(X, Y)操作的时间复杂度是O(n)。

能不能Find-set(a)与Union(X, Y)都在O(1)的时间内完成呢?

可以,这就引发了并查集的第二种实现方法。

二、并查集的有根树实现

什么是有根树,和普通的树有什么不同?

常用的set,就是用普通的二叉树实现的,其元素的数据结构是:

element{

         int data;

         element* left;

         element* right;

}

通过左指针与右指针,父亲节点指向儿子节点。

image.png

而有根树,其元素的数据结构是:

element{

         int data;

         element* parent;

}

通过儿子节点,指向父亲节点。

假设有集合:

S1={7,3,1,4}

S2={1,6}

通过如果通过有根树表示,可能是这样的:

image.png

所有的元素,都通过parent指针指向集合句柄,所有元素的Find-set(a)的时间复杂度也是O(1)。

画外音:假设集合的首个元素,代表集合句柄。

有根树实现的并查集,Union(X, Y)的过程如何?时间复杂度是多少?

通过有根树实现并查集,集合合并时,直接将一个集合句柄,指向另一个集合即可。

image.png

如上图所示,S2的句柄,指向S1的句柄,集合合并完成:S2消亡,S1变为了更大的集合。

容易知道,集合合并的时间复杂度为O(1)。

会发现,集合合并之后,有根树的高度变高了,与“加权合并”的优化思路类似,总是把节点数少的有根树,指向节点数多的有根树(更确切的说,是高度矮的树,指向高度高的树),这个优化叫做“按秩合并”。

新的问题来了,集合合并之后,不是所有元素的Find-set(a)操作都是O(1)了,怎么办?

image.png

如图S1与S2合并后的新S1,首次“通过元素6来找新S1的句柄”,不能在O(1)的时间内完成了,需要两次操作。

但为了让未来“通过元素6来找新S1的句柄”的操作能够在O(1)的时间内完成,在首次进行Find-set(“6”)时,就要将元素6“寻根”路径上的所有元素,都指向集合句柄,如下图。

image.png

某个元素如果不直接指向集合句柄,首次Find-set(a)操作的过程中,会将该路径上的所有元素都直接指向句柄,这个优化叫做“路径压缩”。

画外音:路径上的元素第二次执行Find-set(a)时,时间复杂度就是O(1)了。

实施“路径压缩”优化之后,Find-set的平均时间复杂度仍是O(1)。

结论

通过链表实现并查集:

  • Find-set的时间复杂度,是O(1)常数时间
  • Union的时间复杂度,是集合平均元素个数,即线性时间

画外音:别忘了“加权合并”优化。

通过有根树实现并查集:

  • Union的时间复杂度,是O(1)常数时间
  • Find-set的时间复杂度,通过“按秩合并”与“路径压缩”优化后,平均时间复杂度也是O(1)

使用并查集,非常适合解决“微信群覆盖”问题。

思路比结论重要,有收获就是好的。

image.png
架构师之路-分享可落地的技术文章

目录
相关文章
|
存储 编译器 C语言
【C语言】判断字符类型的三种方法
【C语言】判断字符类型的三种方法
722 0
|
弹性计算 固态存储 大数据
2024阿里云服务器租用价格表(一年/按月/按小时报价明细)
阿里云服务器2024年最新租用价格表显示,轻量应用服务器2核2G3M带宽一年82元(约6.8元/月),2核4G4M带宽轻量服务器一年298元。新老用户共享99元一年的2核2G3M带宽ECS经济型e实例服务器与199元一年的企业专享2核4G5M带宽ECS u1实例服务器优惠。4核16G10M带宽游戏服务器70元/月,8核32G10M带宽160元/月。GPU服务器如gn6v和gn6i等提供新用户专享折扣。续费折扣方面,续费一年享有7.5折,续费五年则有3折优惠。按小时计费的云服务器ECS实例中,如ecs.u1-c1m4.large(2核8G)每小时0.45元。
29656 17
|
测试技术 数据库
腾讯游戏测试工程师的经验心得分享
腾讯游戏测试工程师的经验心得分享
639 0
|
开发工具 对象存储 git
|
JavaScript 前端开发 开发者
哇塞!Vue.js 与 Web Components 携手,掀起前端组件复用风暴,震撼你的开发世界!
【8月更文挑战第30天】这段内容介绍了Vue.js和Web Components在前端开发中的优势及二者结合的可能性。Vue.js提供高效简洁的组件化开发,单个组件包含模板、脚本和样式,方便构建复杂用户界面。Web Components作为新兴技术标准,利用自定义元素、Shadow DOM等技术创建封装性强的自定义HTML元素,实现跨框架复用。结合二者,不仅增强了Web Components的逻辑和交互功能,还实现了Vue.js组件在不同框架中的复用,提高了开发效率和可维护性。未来前端开发中,这种结合将大有可为。
419 0
|
12月前
|
项目管理
项目里程碑定义及重要性解析
项目里程碑是项目管理中的重要工具,用于将复杂项目分解为更小的阶段,明确目标和时间节点,提高管理效率。项目管理软件可辅助创建、跟踪和管理里程碑,确保项目按计划进行。通过设定里程碑,团队可以更好地协调资源,减少不必要的重复工作,确保项目顺利推进。
402 0
|
关系型数据库 分布式数据库 数据库
沉浸式学习PostgreSQL|PolarDB 21,相似图像搜索
传统数据库不支持图像类型, 图像相似计算函数, 图像相似计算操作服, 相似排序操作符. 所以遇到类似的需求, 需要自行编写应用来解决. PG|PolarDB 通过imgsmlr插件, 可以将图像转换为向量特征值, 使用相似距离计算函数得到相似值, 使用索引加速相似度排序, 快速获得相似图片, 实现以图搜图. 也可以通过pgvector插件来存储图片向量特征值, 结合大模型服务(抠图、图像向量转换), 可以实现从图像转换、基于图像的相似向量检索全流程能力.
1106 1
|
消息中间件 缓存 NoSQL
Redisson实现简单消息队列:优雅解决缓存清理冲突
在项目中,缓存是提高应用性能和响应速度的关键手段之一。然而,当多个模块在短时间内发布工单并且需要清理同一个接口的缓存时,容易引发缓存清理冲突,导致缓存失效的问题。为了解决这一难题,我们采用Redisson的消息队列功能,实现了一个简单而高效的消息队列,优雅地解决了缓存清理冲突问题。本文将为您详细介绍Redisson实现简单消息队列的方案,以及如何在项目中使用它来优化缓存清理。
642 0
Redisson实现简单消息队列:优雅解决缓存清理冲突
|
搜索推荐 数据安全/隐私保护 开发者
几个免费PDF处理网站:文件合并、PDF编辑、格式转换…
本文介绍几个方便、免费、好用的PDF在线处理、编辑网站~
484 1
几个免费PDF处理网站:文件合并、PDF编辑、格式转换…
|
存储 人工智能 弹性计算
阿里云高性能计算负责人何万青:阿里云大计算加速HPC与AI融合
与AI相结合,高性能计算能够帮助科研人员将精力集中于专业领域。
阿里云高性能计算负责人何万青:阿里云大计算加速HPC与AI融合