一分钟说清楚并查集

  发布时间:2025-11-04 10:50:01   作者:玩站小弟   我要评论
分离集合(disjoint set)是一种经典的数据结构,它有三类操作:Make-set(a):生成包含一个元素a的集合S; Union(X, Y):合并两个集合X和Y; 。

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

Make-set(a):生成包含一个元素a的说清集合S; Union(X, Y):合并两个集合X和Y; Find-set(a):查找元素a所在集合S,即通过元素找集合句柄;

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

一、说清并查集的楚并查集链表实现

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

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

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

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

假设有集合:

S1={7,3,1,4} S2={1,6} 

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

***个集合的楚并查集尾元素,链向第二个集合的头元素(蓝线1); 第二个集合的所有元素,指向***个集合的源码下载句柄(蓝线2,3);

合并完的效果是:

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

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

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

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

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

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

二、并查集的有根树实现

(1) 什么是有根树,和普通的云服务器提供商树有什么不同?

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

element{          int data;          element* left;          element* right; } 

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

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

element{          int data;          element* parent; } 

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

假设有集合:

S1={7,3,1,4} S2={1,6} 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

结论

通过链表实现并查集:

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

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

通过有根树实现并查集:

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

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

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

【本文为专栏作者“58沈剑”原创稿件,转载请联系原作者】

戳这里,看该作者更多好文

  • Tag:

相关文章

  • 解决宽带错误651调制解调器问题的有效方法(解决宽带错误651的实用技巧和技术指导)

    摘要:在使用宽带上网时,有时我们可能会遇到错误代码651,这通常是由于调制解调器出现问题所致。这个错误会导致无法连接到互联网,给我们的上网体验带来不便。然而,不必担心,本文将为您提供一些...
    2025-11-04
  • Node.js 服务性能翻倍的秘密(一)

    前言用过 Node.js 开发过的同学肯定都上手过 koa,因为他简单优雅的写法,再加上丰富的社区生态,而且现存的许多 Node.js 框架都是基于 koa 进行二次封装的。但是说到性能,就不得不提到
    2025-11-04
  • 如何实现Java类隔离加载?

    Java 开发中,如果不同的 jar 包依赖了某些通用 jar 包的版本不一样,运行时就会因为加载的类跟预期不符合导致报错。如何避免这种情况呢?本文通过分析 jar 包产生冲突的原因及类隔离的实现原理
    2025-11-04
  • 大牛用VScode写C/C++嘛?VScode集成MinGW

    下载MinGWMinGW 是一款开源C/C++编译套件,允许你在GNU/Linux和Windows平台生成本地的Windows程序而不需要第三方C运行时(C Runtime)库。进入https://s
    2025-11-04
  • 电脑蓝屏错误时间的解决方法(提高电脑稳定性,有效应对蓝屏错误)

    摘要:电脑蓝屏错误是使用电脑过程中常见的问题,给我们的工作和生活带来了不便。如何解决电脑蓝屏错误,提高电脑的稳定性,是我们需要掌握的重要技能。本文将从不同方面介绍解决电脑蓝屏错误的方法和...
    2025-11-04
  • 9个好用的JavaScript小技巧

    黑客的方法论是一种涉及不断改进和迭代的构建方法。黑客们认为总有一天会变得更好,而且没有什么是永远不能够实现的。真正的黑客总是用不同的方式来解决没人关注的问题。下面给出了一些非常强大的 JavaScri
    2025-11-04

最新评论