题目描述:
题解:

可以看到,如果节点是相互连通的(从一个节点可以到达另一个节点),那么他们在同一棵树里,或者说在同一个集合里,或者说他们的祖先是相同的。
(1)初始化
当把一个新节点添加到并查集中,它的父节点应该为空,同时集合数量+1
(2)合并两个节点
如果发现两个节点是连通的,那么就要把他们合并,也就是他们的祖先是相同的。谁做父节点一般是没有区别的。同时集合数量-1
(3)查找祖先和路径压缩
如果节点的父节点不为空,那就不断迭代
这里有一个优化的点:如果我们树很深,比如说退化成链表,那么每次查询的效率都会非常低。所以我们要做一下路径压缩。也就是把树的深度固定为二。
这么做可行的原因是,并查集只是记录了节点之间的连通关系,而节点相互连通只需要有一个相同的祖先就可以了。
class UnionFind:def __init__(self):# 记录父结点self.father = {}# 记录集合的数量self.num_of_sets = 0def 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] = rootx = original_fatherreturn root#聚合两个结点def merge(self,x,y):root_x,root_y = self.find(x),self.find(y)#如果父结点不同,则合并if root_x != root_y:self.father[root_x] = root_y# 集合的数量-1self.num_of_sets -= 1def add(self,x):if x not in self.father:self.father[x] = None# 集合的数量+1self.num_of_sets += 1class Solution:def findCircleNum(self, M: List[List[int]]) -> int:uf = UnionFind()for i in range(len(M)):uf.add(i)for j in range(i):if M[i][j]:uf.merge(i,j)return uf.num_of_sets


