最近在学习算法,虽然看书觉得理论都知道,可是一去刷题就不会,不知道咋用,所以打算每刷一题,便将其记录下来,用于以后的自我回顾。这次选择从并查集(Leetcode1319)开始
-
并查集通俗概念
在网上可以搜出一堆很专业术语,这里写下我自己的理解:即将彼此孤立的点先划分为单个集合,如果某两点之间有相同的关系,就放到一个集合里。就像地里一堆水果。是苹果一篮,梨一篮。如果在放大一些,梨和苹果又是可以
构成一个集合,都是果树。这里提完了并,在说下查的过程,就是任取一个点,通过它可以找到它所属最终的根,即你这果子最后是属于哪个果树。如果我们都是苹果,但之前没有我们没划分到一个集合里,那么我们合并,否则直接返回。如果没有合并的条件,是不会执行这个并操作的
-
并查集通用代码段
1. 初始化
每个点且赋值 -1 ,看题解的python代码段也可以使用list(range(n))初始化为s[n]=[0,1,2].按序递增,差别就在后面find函数的判断里,后续代码将采用一致为-1作每个节点根节点的S[x]值
s = [-1 for _ in range(n)]
2. Find 函数:查找目标结点的根结点
def find(x):
while s[x]>=0:
x=s[x]
return x
如果你这个结点有进行合并过操作,则s[x]的值为其父结点的下标值,那将恒为正,只要满足正值,表示并未找到真正的根节点,此时一直找下去,直至-1的位置
此方法在树并不是很合理的方式,如所有的节点都连成了一条线,将导致时间有点长,为此将其代码进行了优化
Find优化版—压缩路径:
- 逻辑是第一次循环先找目标点的根节点,在第二次循环依次将目标节点及其父节点都直接挂到根目录下,这样达到路径压缩目的,如果下次找这条路径上的点,都能一下从根节点找到
如图:
def find(x):
root=x
while(self.parent[root]>=0):
root=self.parent[root]
while(x!=root):
t=self.parent[x]
self.parent[x]=root
x=t
return root
这里首先将x给root是为了防止第一次循环改变x值,所以当真正找到了root就可以和x在第二次使用判定是否已经在root下
3. Union函数
只要函数具有关联能放到一个集合里,就直接将node1作为node2的根节点
def unin(self,node1,node2):
s[node2]=node1
上面这段代码会出现某种问题,如果大树挂到小树上会出现高度增加,但是为了维持树的高度不变,那可以将小树挂到大树上,这样树的高不变,只是宽度增加
Union优化版—小树挂大树:
def Union(self,node1,node2): ##采用子结点小的放到大的,即让树横着长
if node1==node2:
return
rootx=self.find(node1)
rooty=self.find(node2)
if self.s[rooty]>self.s[rootx]:
self.s[rootx]+=self.s[rooty]
self.s[rooty]=rootx
else:
self.s[rooty]+=self.s[rootx]
self.s[rootx]=rooty
-
Leetcode1319
class UnionFind:
def __init__(self, n: int):
self.parent = [-1 for _ in range(n)]
self.setCount = n
def find(self,x): ##采用路径压缩,将其查找的的父节点直至根,都直接放到根结点下面
root=x
while(self.parent[root]>=0):
root=self.parent[root]
while(x!=root):
t=self.parent[x]
self.parent[x]=root
x=t
return root
def unin(self,x,y): ##采用子结点小的放到大的,即让树横着长
rootx=self.find(x)
rooty=self.find(y)
if rootx == rooty:
return False
if self.parent[rooty]>self.parent[rootx]:
self.parent[rootx]+=self.parent[rooty]
self.parent[rooty]=rootx
self.setCount -= 1
else:
self.parent[rooty]+=self.parent[rootx]
self.parent[rootx]=rooty
self.setCount -= 1
return True
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) <n-1:
return -1
uf = UnionFind(n)
for x, y in connections:
uf.unin(x,y)
return uf.setCount -1