对并查集的理解与相关操作

并查集

作用

  1. 将两个集合合并
  2. 询问两个元素是否在一个集合当中(或是否根出同源)。
    常见用途有求连通子图、求最小生成树的 K r u s k a l Kruskal Kruskal算法和求最近公共祖先(LCA)等。

基本原理

每一个集合用一个树来表示,树根的编号就是整个集合的编号,每个节点存储它的父节点, p [ x ] p[x] p[x]表示 x x x的父节点。

主要问题

  1. 树根的定义: i f ( p [ x ] = = x ) if(p[x]==x) if(p[x]==x)
  2. 如何求x的集合编号,即找到树根:find操作
  3. 如何合并两个集合

基于以上几个问题,产生下列几个操作。

基本操作

1. 初始化 i n i t init init

首先将给出的数据以自身作为集合(以自己为下标指向自己),创建一个数组,记为p,即

nums = list()	# 数据内容
p = [0 for _ in range(len(nums))]	# p数组(大小为nums的长度)
for num in nums:
	p[num] = num

图像解释:
初始化

在一系列合并操作后,某一节点仍然指向自己,即 p [ n u m ] = = n u m p[num] == num p[num]==num,则说明该节点没有被合并到其他节点上(无父节点),要么它是孤零零没被操作(在合并完所有数一般没有这种情况(以我贫瘠的知识储备没有遇到过,欢迎举例指出)),要么就是根节点(是其他节点的祖宗),所以树根被定义为 i f ( p [ x ] = = x ) if(p[x]==x) if(p[x]==x)

2. 查询 f i n d find find

有三种方法

1. 直接迭代

从要查找的数(记为x)开始,向x的父节点开始查询,直到父节点等于本身,即 p [ x ] = = x p[x] == x p[x]==x,说明找到了树根。

Code
def find(x):
	while p[x] != x:
		x = p[x]	# 继续遍历父节点
	return x

这种方法时间复杂度比较高,容易TLE,所以不建议使用。

2. 直接迭代 + 路径压缩

路径压缩:如果出现要重复查询x的祖宗节点,在不用路径压缩的情况下,就要重复查询的次数次,时间消耗多,若树较长,则容易TLE。所以,为节省时间,在第一次查询后,直接将x到根节点之间的所有节点(包括x)直接指向根节点,则在下一次查询x与x到根节点之间的数时,只用向上找一次就可找到根节点
路径压缩

Code
def find(x):
	j = 0
	k = 0
	i = x	# 用i找到x的祖宗节点
	while p[i] != i:
		i = p[i]
	k = x	# k用来从x开始一层层向上遍历
	# 路径压缩
	while k != i:
		j = p[k]	# j存储k当前的父节点
		p[k] = i	# 让k的父节点指向祖宗节点
		k = j		# 让k继续向上遍历
	return i
3. 递归
Code
def find(x):
	if p[x] == x:
		return x
	else:
		return find(p[x])

还是容易TLE

4. 递归 + 路径压缩
Code
def find(x):
	if p[x] != x:
		# find会一直递归下去直到找到根节点,最后返回根节点给当前的p[x]
		# 	对于x到根节点的所有数也相同,都将父节点改为根节点
		p[x] = find(p[x])
	return p[x]

3. 合并 u n i o n n unionn unionn

将该集合的根节点指向另一集合的根节点即可。
合并操作

Code
def unionn(x, y):
	p[find(x)] = find(y)

如果还没有看懂,推荐这个视频,更加全面细节,点这
接下来还会更新并查集的相关做题笔记,欢迎关注,提示更新。

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值