数据结构与Python算法——并查集

并查集是解决图的遍历问题的一种优化数据结构,在元素的划分和查找问题中,可以有效降低解决问题时的时间复杂度。

并查集介绍:

并查集是一种表示不相交集合的数据结构。并查集由一组彼此之间元素各不相同的集合组成,常用于表示一组不相交元素构成的动态集合。
在并查集结构中,每个集合都有一个代表元素,用于表示该集合。代表元素可在集合内的元素中任意选取。

并查集的构造方法

并查集中包含 3种基础操作,包括构建新的集合(MAKE-SET)、合并集合(UNION)和查找某个元素所在的集合(FIND-SET)。
设x、y分别为并查集中任意集合中的元素,则各操作详情如下:
(1) MAKE-SET(x):构建新的集合。
在初始化阶段,需要将元素构建成并查集结构。在此过程中,为所有的元素都建立一个独立的集合,每个新集合中仅包含一个元素,即对于 MAKE-SET(x)过程,构建一个只包含x元素的集合。由于并查集中的各个集合不相交,因此,元素x不会出现在其他集合中。
(2) UNION(x, y):合并两个集合。
该过程中,将x元素所在的集合Sx和y元素所在的集合Sy合并为一个新的集合。为了保持并查集中各个集合互不相交的特性,需要在完成合并后删除原集合Sx和Sy,同时,从新集合SxUSy中意选取一个元素作为新集合的代表元素。
在实际操作中,为了提升执行效率,通常将其中一个集合并入另一个集合中来代替删除操作。
(3) FIND-SET(x):查找某元素所在的集合。
在该过程中,给定元素x,需要返回一个指针,该指针指向包含x的集合的代表。

并查集的应用

并查集常用于在无向图中查找连通分量,延申应用包括朋友圈查找、犯罪团伙判断等。
在解决上述问题时,通过常规的图算法,如广度优先遍历、深度优先遍历等,也可以实现目标。但是有些问题需要不断将集合合并,而且要不断查找某个元素是否在某个集合中。若用传统方式解决,一方面空间复杂度很高,需要消耗大量的存储空间;另一方面时间复杂度也很高,很难在短时间内获得结果,在处理较大规模的集合时甚至往往无法顺利解决问题。并查集利用其特有的结构特点,可以在构造并查集的过程中对结构进行优化,方便后续查找操作的进行,可有效减少对解决问题无帮助的内容的检索,有效提升了解决方案的执行效率,降低了算法的时间和空间复杂度。
下面,我们通过一个具体问题来说明并查集的应用:查找亲戚关系问题。
给定n个人,其中存在 m对亲戚关系。在这里,亲戚关系统一遵循以下原则——若甲和乙是亲戚,乙和丙是亲戚,那么可知甲和丙也是亲戚关系。
根据上述条件,求给定的k对人员之间是不是亲戚关系。
9个人(分别用a、b、 、i 表示)存在7对亲戚关系,分别为(a,b)、(a,d)、(b,d)、(c,d)、(e,f)、(g,i)、(g,h),求(a,c)和(b,g)之间是否存在亲戚关系。
图算法解题思路:构造图结构,用节点表示个人,用边表示人与人之间的亲戚关系,构造出包含 n=9个节点和 m=7 条边的图。解决问题时,对k=2个要判断的关系,分别检查关联的两个人是否在一个连通子图上来判断他们是否存在亲戚关系。
图算法难点:若n和m 过大,会导致图的构造和遍历过程占用大量的空间和时间,很难在可接受的时间内完成或者可能根本无法获得结果。另外,用图来解决该问题有些大材小用。例如若给定的人数n较大,且已知的关系对m较大,则需要构建的图的规模就很大,而此时若要求的k 对关系取值较小,则原本构建的很多关系是多余的,从而造成时间和空间资源的浪费。构造图的过程对求解时的图遍历过程没有助力,无法有效利用图构造过程中的信息来优化后续的关系求解问题。
并查集解决方案:引入并查集后,我们换一种解题思路,通过 MAKE-SET 构造并查集,通过UNION 表示n个人之间的m对关系,之后通过 FIND-SET 判断k对亲戚关系。具体步骤如下:
先是通过 MAKE-SET 过程将所有9个人构造成9个独立集合,如下表所示:
在这里插入图片描述
之后,不断遍历7条亲戚关系,每次迭代都通过 UNION 过程将有亲戚关系的两个集合进行合并,并随机选出两个集合中的一个代表作为新集合的代表。各 UNION 迭代之后的并查集状态如下表所示。处理完已知的7条关系,可获得3个互不相交的集合:{a,b,c,d}、{e,f}和{g,i,h},与用图算法获取的连通子图结果一致。
在这里插入图片描述
获得最终的并查集结果后,可以进行(a,c)以及(b,g)两对关系是否为亲戚关系的判断。该操作等价于判断(a,c)两元素和(b,g)两元素是否在同一个集合中的问题。通过 FIND-SET 操作,分别定位元素a,c,b,g所在集合的代表元素,通过判断(a,c)两元素和(b,g)两元素所在集合的代表元素是否相同即可分别判断它们是否属于一个子集,从而获知是否 为亲戚关系。在本例中,若设定结果集合中的第一个元素为该集合的代表,则a和c所属集合的代表都是 a,说明两者在一个集合中,是亲戚关系;对于b和g,由于b所属集合的代表是a,而g所属集合的代表是g,两者不相等,说明两者不在一个集合中,不是亲戚关系。
在查找过程中,可以记录每次迭代过程中所获得的当前元素的代表,对检索路径长度进行压缩,为后续查找提升效率。

并查集3种基本操作的 Python 实现

在实现过程中,维护两个数组分别保存各元素的所属集合的代表元素和集合的大小,分别用fatherList 和 sizeList 表示。
MAKE-SET(x)过程,初始化每个节点的代表元素为自身,每个节点各自组成一个集合。
FIND-SET(x)过程,用递归的方式获取当前元素x所属集合的代表元素,同时在该过程中进行路径压缩。
UNION 过程,不断将两个集合进行合并,选择较大集合的代表元素作力新集合的代表元素,即修改较小集合中各个元素的代表元素信息,可减小改动量,提升操作效率。
并查集的3种基本操作代码:
在这里插入图片描述

  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值