引言
在刷算法题的时候, 遇到了并查集这个数据结构。对这个数据结构不是特别熟悉,在这篇文章中专门整理总结一下。
这篇文章主要关注以下几点:
- 并查集可以用于哪些场景
- 并查集有哪些操作
- 并查集的简单实现方式
- 实际解决问题的案例
并查集的应用场景
个人理解,并查集最直接的应用,还是用于分组问题, 把一个几个分成若干个组, 每个组都有一个代表。 我们可能需要把两个组给合并然后重新选举一个代表, 也可能会在给定一个元素的场景下, 判断这个元素是否在某个组中。
看了一些参考资料, 并查集的主要用途有:
- 维护无向图的连通性, 判断两个点是否在同一个连通块内。
- 判断增加一条边是否会产生环。 (对这个用途还不是特别了解,后续研究)
并查集的操作
-
makeSet()
表示建立一个并查集,这通常是一个数组或者字典,我们可以用索引表示元素的id, 再用数组元素的值表示这个组的代码元素id。 -
unionSet(x, y)
把元素x 和元素y 的所在的集合合并。 -
find(x)
找到元素x所在的集合代表。
并查集的数据结构实现方式
我们来实现一下并查集的方法。
# 使用并查集的方法编程
class UnionFind:
"""
* 这里并查集定义为一个字典
* key是元素id, value 是这个组的父节点id, 也就是每个分组的代表。
* 记录一下组的个数
"""
def __init__(self):
self.father = {}
self.num_of_sets = 0
"""
目的是返回元素x所在的组的代表
* 第一步先找到树的根
* 第二步, 每一次遍历父节点,所有节点的父节点都变为根节点
这是在查找过程中不断的更新树。
"""
def find(self, x):
root = x
while self.father[root] != None:
root = self.father[root]
while x != root:
original_father = self.father[x]
self.father[x] = root
x = original_father
return root
"""
unionSet 操作
* 查询x,y两个节点的根节点, 如果不是在同一个组中,就合并, 其实就是把根节点设置为一个
"""
def merge(self, x, y):
root_x, root_y = self.find(x), self.find(y)
print(root_x, root_y)
if root_x != root_y: # 这里存疑, 应该是相等吧
# print(root_x, root_y)
self.father[root_x] = root_y
self.num_of_sets -= 1
"""
添加元素的操作也比较简单, 这个新元素就是根节点, 单独作为一个组。
组的个数 加一。
"""
def add(self, x):
if x not in self.father:
self.father[x] = None
self.num_of_sets += 1
算法题目案例
题目:朋友圈问题, leetcode题目编号(#547),给定一个二维数组,isConnected[i][j]表示 i和j是不是朋友, 1表示是朋友。求朋友圈的个数。
思路: 遍历二维数组中的所有元素,对于每个人,先把这个人加入到并查集中, 再看看其他人和这个人是否是朋友,如果是,则执行合并操作。
uf = UnionFind()
for i in range(len(isConnected)):
uf.add(i)
for j in range(i):
if isConnect[i][j]:
uf.merge(i,j)
return uf.num_of_sets
总结
并查集在图算法中也有很多的应用, 后续可以学习了解下。