文章目录
前言
并查集
一、并查集
并查集一共可以分为三步:
1.初始化
2.合并
3.查询
首先是初始化:
def init(length:int)->List[int]:
return list(range(length))
用一个数组parent 来表示 parent[i] = j 表示 i的先辈是j。初始化是parent[i] = i,表示自己是自己的先辈。
接着是合并,例如3是4的先辈,2又是3的先辈。1又是2 的先辈 间接可以得出1又是4的先辈:如下图
合并就是根据需要合并的条件,将节点union起来。
最后是查询。其实查询与合并几乎是同时进行的,查询是寻找当前节点的先辈。如果新的节点连接起来,那么当前节点的的先辈,就要变换啦,变换为新的先辈。查询过程也是递归查询,代码如下:
#find 找该节点的祖先
def find(index:int)->int:
if parent[index]!=index:
parent[index] = find(parent[index])
return parent[index]
二、LeetCode 例题
1.547.省份的数量
解题思路:
#使用并查集的思想,将省份联通,因为联通的省份的先辈会变成别人。
# 最后统计 有多少个 parent[i] = i 就可以啦
并查集解题都很有模板可循,代码如下:
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
"""
# 并查集
"""
def init(length:int)->List[int]:
return list(range(length))
#find 找该节点的祖先
def find(index:int)->int:
if parent[index]!=index:
parent[index] = find(parent[index])
return parent[index]
#合并
def union(index1:int,index2:int):
parent[find(index1)] = find(index2)
length = len(isConnected)
parent = init(length)
for i in range(length):
for j in range(i+1,length):
if isConnected[i][j]==1:
union(i,j)
ans = sum(parent[i]==i for i in range(length))
print(parent) #这里可以输出看一下。
return ans
2.684.冗余连接
解题思路:
#同样使用并查集,将边联通起来,在连接时,判断两个节点是否已经联通,
#如果已经联通,则返回两个节点的值
# 否则继续连接
也是套模板的样子,代码如下:
class Solution:
def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
def init(length:int)->List[int]:
return list(range(length))
#find 找该节点的祖先
def find(index:int)->int:
if parent[index]!=index:
parent[index] = find(parent[index])
return parent[index]
#合并
def union(index1:int,index2:int):
parent[find(index1)] = find(index2)
length = len(edges)
parent = init(length+1)
for i in range(length):
if find(edges[i][0])!= find(edges[i][1]):
union(edges[i][0],edges[i][1])
else:
return [edges[i][0],edges[i][1]]
return []
总结
这是一个套模板的算法,大家理解就好。主要是注意查询和连接的条件啦~