题目描述:

image.png

image.png
image.png

题解:

image.png
可以看到,如果节点是相互连通的(从一个节点可以到达另一个节点),那么他们在同一棵树里,或者说在同一个集合里,或者说他们的祖先是相同的

(1)初始化
当把一个新节点添加到并查集中,它的父节点应该为空,同时集合数量+1
(2)合并两个节点
如果发现两个节点是连通的,那么就要把他们合并,也就是他们的祖先是相同的。谁做父节点一般是没有区别的。同时集合数量-1
(3)查找祖先和路径压缩
image.png
如果节点的父节点不为空,那就不断迭代
这里有一个优化的点:如果我们树很深,比如说退化成链表,那么每次查询的效率都会非常低。所以我们要做一下路径压缩。也就是把树的深度固定为二。

这么做可行的原因是,并查集只是记录了节点之间的连通关系,而节点相互连通只需要有一个相同的祖先就可以了。

  1. class UnionFind:
  2. def __init__(self):
  3. # 记录父结点
  4. self.father = {}
  5. # 记录集合的数量
  6. self.num_of_sets = 0
  7. def find(self,x):
  8. root = x
  9. #寻找父结点
  10. while self.father[root] != None:
  11. root = self.father[root]
  12. #路径压缩
  13. while x != root:
  14. original_father = self.father[x]
  15. self.father[x] = root
  16. x = original_father
  17. return root
  18. #聚合两个结点
  19. def merge(self,x,y):
  20. root_x,root_y = self.find(x),self.find(y)
  21. #如果父结点不同,则合并
  22. if root_x != root_y:
  23. self.father[root_x] = root_y
  24. # 集合的数量-1
  25. self.num_of_sets -= 1
  26. def add(self,x):
  27. if x not in self.father:
  28. self.father[x] = None
  29. # 集合的数量+1
  30. self.num_of_sets += 1
  31. class Solution:
  32. def findCircleNum(self, M: List[List[int]]) -> int:
  33. uf = UnionFind()
  34. for i in range(len(M)):
  35. uf.add(i)
  36. for j in range(i):
  37. if M[i][j]:
  38. uf.merge(i,j)
  39. return uf.num_of_sets