python实现并查集
由于想刷算法题的时候能够更方便,于是自己实现了并查集,企图做题的时候可以直接复制粘贴套用自己做的轮子
并查集其实本身是森林,不是图,不需要什么广度优先遍历。好像还蛮快的
但是还有代优化,比如压缩路径还没有做。只做了合并路径时候的一点优化。
具体是看知乎上赞最多的学的 https://zhuanlan.zhihu.com/p/93647900
代码
调用方法,使用方法:
import random
# 待插入轮子代码
def main():
s = MergeSetInt(10)
for i in range(20):
a, b = random.randint(0, 9), random.randint(0, 9)
print("连接", a, b)
s.union(a, b)
s.show()
a, b = random.randint(0, 9), random.randint(0, 9)
print(a, b, "是否联通:", s.find(a, b))
if __name__ == '__main__':
main()
轮子代码
import random
class MergeSetInt:
"""
并查集简单实现
元素由 [0,n) 连续的序号组成
有连接和判断连接两个功能
需要引入随机库
"""
class Node:
def __init__(self, val: int):
"""创建一个初始化的 0 ~ val 的并查集 """
self.val = val
self.father = self
self.deep = 1
def getFather(self):
"""获取这个节点的父节点,同时可以压缩路径"""
node = self
while node.father != node:
node = node.father
return node
def __init__(self, n):
"""生成一个内部含序号为 0到n-1的集合"""
self._list = [self.Node(i) for i in range(n)]
def union(self, m: int, n: int) -> bool:
"""合并操作"""
fatherNode1 = self._list[m].getFather()
fatherNode2 = self._list[n].getFather()
if fatherNode1 == fatherNode2:
# 已经在统一个集合里了
return False
if fatherNode1.deep < fatherNode2.deep:
# 1 从了 2
fatherNode1.father = fatherNode2
fatherNode2.deep = max(fatherNode1.deep + 1, fatherNode2.deep) # 更新高度
elif fatherNode1.deep > fatherNode2.deep:
# 2 从了 1
fatherNode2.father = fatherNode1
fatherNode1.deep = max(fatherNode1.deep, fatherNode2.deep + 1)
else:
# 如果两个树一样高,就可以随机选一个树从了另一个树
if random.random() < 0.5:
fatherNode1.father = fatherNode2
fatherNode2.deep = max(fatherNode1.deep + 1, fatherNode2.deep) # 更新高度
else:
fatherNode2.father = fatherNode1
fatherNode1.deep = max(fatherNode1.deep, fatherNode2.deep + 1)
return True
def show(self):
for node in self._list:
while node.father is not node:
print(node.val, end="->")
node = node.father
print(node.val)
print()
def find(self, m: int, n: int):
fatherNode1 = self._list[m].getFather()
fatherNode2 = self._list[n].getFather()
return fatherNode1 == fatherNode2
普适性更新
更新内容:
- 可以添加元素了,一开始创建的是空并查集
- 可以获取并查集中最大的集合的大小
class UnionFindSet:
"""
数字类型并查集
一开始生成空的并查集
可以添加元素、连接元素、判断两个元素是否连接
添加的元素类型是数字类型,元素和元素之间是不重复的
"""
class Node:
def __init__(self, val: int):
self.val = val
self.father = self
self.deep = 1
self.groupCount = 1 # 包括自身,节点的数量
def getFather(self):
"""获取这个节点的父节点
待更新:压缩路径"""
node = self
while node.father != node:
node = node.father
return node
def __hash__(self):
return self.val
def __init__(self):
"""初始化一个空的数字并查集"""
self.dic = {}
def addNode(self, val: int):
"""向并查集中添加一个元素,这个元素必须是数字类型"""
self.dic[val] = self.Node(val)
def maxGroupNum(self):
"""
获得数量最大的集合 的大小
例如:
{ {1} {2} {3} {4} } >>> 1
{ {1,2,3} {4} } >>> 3
时间复杂度为On,待更新:简化为O1
"""
res = 1
for k, v in self.dic.items():
if v.groupCount > res:
res = v.groupCount
return res
def union(self, m: int, n: int) -> bool:
"""
合并操作
将并查集中的两个元素合并
"""
if m not in self.dic or n not in self.dic:
return False
fatherNode1 = self.dic[m].getFather()
fatherNode2 = self.dic[n].getFather()
def con1to2():
# print("12...")
# 1 从了 2
fatherNode1.father = fatherNode2
fatherNode2.deep = max(fatherNode1.deep + 1, fatherNode2.deep) # 更新高度
fatherNode2.groupCount += fatherNode1.groupCount
def con2to1():
# print("21...")
# 2 从了 1
fatherNode2.father = fatherNode1
fatherNode1.deep = max(fatherNode1.deep, fatherNode2.deep + 1)
fatherNode1.groupCount += fatherNode2.groupCount
if fatherNode1 == fatherNode2:
# 已经在统一个集合里了
# print("have")
return False
if fatherNode1.deep < fatherNode2.deep:
# print("a")
con1to2()
return True
if fatherNode1.deep > fatherNode2.deep:
# print("aa")
con2to1()
return True
else:
# 如果两个树一样高,就可以随机选一个树从了另一个树
# print("aaa")
if random.random() < 0.5:
con1to2()
return True
else:
con2to1()
return True
def show(self):
"""展示并查集"""
for k, node in self.dic.items():
while node.father is not node:
print(node.val, end="->")
node = node.father
print(node.val)
print()
def find(self, m: int, n: int):
"""检验连通"""
if m not in self.dic or n not in self.dic:
return False
fatherNode1 = self.dic[m].getFather()
fatherNode2 = self.dic[n].getFather()
return fatherNode1 == fatherNode2