模板:

  1. class UnionFind:
  2. def __init__(self,n):
  3. self.parent = [i for i in range(n)]
  4. self.rank = [1 for _ in range(n)]
  5. self.count = n
  6. def find(self,p):
  7. while self.parent[p] != self.parent[self.parent[p]]:
  8. self.parent[p] = self.parent[self.parent[p]]
  9. return self.parent[p]
  10. def union(self,p,q):
  11. i = self.find(p)
  12. j = self.find(q)
  13. if i == j:
  14. return
  15. if self.rank [i] < self.rank [j]:
  16. self.parent[i] = j
  17. self.rank [j] += self.rank [i]
  18. else:
  19. self.parent[j] = i
  20. self.rank [i] += self.rank [j]
  21. self.count -= 1

并查集unionfind:

能够解决 朋友圈,岛屿等问题。
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。常常在使用中以森林来表示。
并查集有三种基本操作,获得根节点判断两节点是否连通以及将两不连通的节点相连(相当于将两节点各自的集合合并)·

  • 获得根节点

    1. def get_root(self, i)
  • 判断两节点是否联通

    1. def is_connected(self, i, j)
  • 不连通的节点相连

    1. def union(self, i, j)

    1.1 初始化

    1. class Solution:
    2. def __init__(self, n):
    3. self.parent = list(range(n))

    1.2 获得根节点

    由于parent[i]仅表示自己的直接父节点,查询两个节点是否相交需要比较它们的根节点是否相同。因此要封装一个查询自己根节点的方法。

    1. def get_root(self, i):
    2. while i != self.parent[i]:
    3. i = self.parent[i]
    4. return i

    当前每次执行get_root时,需要一层一层的找到自己的父节点,很费时。由于根节点没有父节点,并且文章开始处提到过如果一个节点没有父节点,那么它的父节点就是自己,因此可以说只有根节点的父节点是自己本身。现在我们加上一个判断,判断当前节点的父节点是否为根节点,如果不为根节点,就递归地将自己的父节点设置为根节点,最后返回自己的父节点。

    1. def get_root(self, i):
    2. if self.parent[i] != self.parent[self.parent[i]]:
    3. self.parent[i] = self.get_root(self.parent[i])
    4. return self.parent[i]

    1.3 比较根节点是否相同来判断两节点是否连通。

    1. def is_connected(self, i, j):
    2. return self.get_root(i) == self.get_root(j)

    1.4 当要连通两个节点时,我们要将其中一个节点的根节点的parent,设置为另一个节点的根节点。

    注意,连通两个节点并非仅仅让两节点自身相连,实际上是让它们所属的集合实现合并。

    1. def union(self, i, j):
    2. i_root = self.get_root(i)
    3. j_root = self.get_root(j)
    4. self.parent[i_root] = j_root

    由于调用get_root时需要通过不断找自己的直接父节点,来寻找根节点,如果这棵树的层级过深,会导致性能受到严重影响。因此我们需要在union时,尽可能的减小合并后的树的高度。
    因此,当合并树时,分别获得节点i和节点j的root i_root和j_root之后,我们通过访问rank[i_root]和rank[j_root]来比较两棵树的高度,将高度较小的那棵连到高度较高的那棵上。如果高度相等,则可以随便,并将rank值加一。

    1. def union(self, i, j):
    2. i_root = self.get_root(i)
    3. j_root = self.get_root(j)
    4. if self.rank[i_root] == self.rank[j_root]:
    5. self.parent[i_root] = j_root
    6. self.rank[j_root] += 1
    7. elif self.rank[i_root] > self.rank[j_root]:
    8. self.parent[j_root] = i_root
    9. else:
    10. self.parent[i_root] = j_root

    例1. pyq任务:

    只给你朋友圈的矩阵,返回朋友圈数量 ```python class DisjointSet: def init(self,n):

    1. ## 初始化parent和rank
    2. self.parent = [i for i in range(n)]
    3. self.rank = [1 for _ in range(n)]
    4. self.count = n

    def find(self,p):

    1. ### 找到root
    2. while self.parent[p] != self.parent[self.parent[p]]:
    3. self.parent[p] = self.parent[self.parent[p]]
    4. return self.parent[p]

    def union(self,p,q):

    1. ### 首先,p和q一定是领边的
    2. ## 先找到p,q的爹
    3. i = self.find(p)
    4. j = self.find(q)
    5. # 如果p,q的爹一样,说明在同一个union里面
    6. if i == j:
    7. return
    8. #如果p,q的爹不一样,p的爹和q的爹比较rank,让p or q都接在大的rank上
    9. # 更新接完后爹的rank
    10. # 做一次不同爹更新意味着少了一个union,count-1
    11. if self.rank [i] < self.rank [j]:
    12. self.parent[i] = j
    13. self.rank [j] += self.rank [i]
    14. else:
    15. self.parent[j] = i
    16. self.rank [i] += self.rank [j]
    17. self.count -= 1

主函数,就不写在class Solution里面了

def findCircleNum( M): “”” :type M: List[List[int]] :rtype: int “”” disjoint_set = DisjointSet(len(M)) for i in range(len(M)): for j in range(i+1,len(M)): if M[i][j] == 1 : disjoint_set.union(i,j) return disjoint_set.count

if name == ‘main‘: M=[[1,1,0],[1,1,0],[0,0,1] M=[[1,0,0,1],[0,1,1,0],[0,1,1,1],[1,0,1,1]] disjoint_set.count=findCircleNum(M) print(disjoint_set) print(‘num of pyq:’, disjoint_set.count)

  1. <a name="YgJh1"></a>
  2. ## 例2. 岛问题
  3. ```python
  4. class DisjointSet:
  5. def __init__(self,n):
  6. ## 初始化parent和rank
  7. self.parent = [i for i in range(n+1)]
  8. self.rank = [1 for _ in range(n)]
  9. self.rank.append(3)
  10. self.count = n
  11. def find(self,p):
  12. ### 找到root
  13. while self.parent[p] != self.parent[self.parent[p]]:
  14. self.parent[p] = self.parent[self.parent[p]]
  15. return self.parent[p]
  16. def union(self,p,q):
  17. ### 首先,p和q一定是领边的
  18. ## 先找到p,q的爹
  19. i = self.find(p)
  20. j = self.find(q)
  21. # 如果p,q的爹一样,说明在同一个union里面
  22. if i == j:
  23. return
  24. #如果p,q的爹不一样,p的爹和q的爹比较rank,让p or q都接在大的rank上
  25. # 更新接完后爹的rank
  26. if self.rank [i] < self.rank [j]:
  27. self.parent[i] = j
  28. self.rank [j] += self.rank [i]
  29. else:
  30. self.parent[j] = i
  31. self.rank [i] += self.rank [j]
  32. self.count -= 1
  33. class Solution:
  34. def numIslands(self, grid: List[List[str]]) -> int:
  35. n=len(grid)
  36. if n==0:return 0
  37. m=len(grid[0])
  38. num_node= n*m
  39. def get_index(i,j):
  40. return i*m+j
  41. island= DisjointSet(num_node)
  42. for i in range(n):
  43. for j in range(m):
  44. if grid[i][j]=="0":
  45. island.union(get_index(i,j),num_node)
  46. else:
  47. if i>0 and grid[i-1][j]=="1" :
  48. island.union(get_index(i,j),get_index(i-1,j))
  49. if j>0 and grid[i][j-1]=="1" :
  50. island.union(get_index(i,j),get_index(i,j-1))
  51. return island.count

有一个二维矩阵 grid ,每个位置要么是陆地(记号为 0 )要么是水域(记号为 1 )。
我们从一块陆地出发,每次可以往上下左右 4 个方向相邻区域走,能走到的所有陆地区域,我们将其称为一座「岛屿」。
如果一座岛屿 完全 由水域包围,即陆地边缘上下左右所有相邻区域都是水域,那么我们将其称为 「封闭岛屿」。
请返回封闭岛屿的数目。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-closed-islands
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  1. class unionfind():
  2. def __init__(self,num):
  3. self.parent=[i for i in range(num)]
  4. self.rank=[1 for _ in range(num)]
  5. self.rank[-1]=2
  6. self.count=num
  7. def find(self,p):
  8. if self.parent[p]!= self.parent[self.parent[p]]:
  9. self.parent[p]=self.parent[self.parent[p]]
  10. return self.parent[p]
  11. def is_connected(self,p,q):
  12. return self.find(p)==self.find(q)
  13. def union(self,p,q):
  14. i=self.find(p)
  15. j=self.find(q)
  16. if i==j:
  17. return
  18. else:
  19. if self.rank[i]<self.rank[j]:
  20. self.parent[i]=j
  21. self.rank[j] +=self.rank[i]
  22. else:
  23. self.parent[j]=i
  24. self.rank[i]+=self.rank[j]
  25. self.count -=1
  26. class Solution:
  27. def closedIsland(self, grid: List[List[int]]) -> int:
  28. n=len(grid)
  29. if n==0: return 0
  30. m=len(grid[0])
  31. uf=unionfind(n*m+1)
  32. def get_index(h,k):
  33. return h*m+k
  34. for i in range(n):
  35. for j in range(m):
  36. if i==0 or j==0 or i==n-1 or j==m-1:
  37. uf.union(get_index(i,j), n*m )
  38. if grid[i][j]==0:
  39. if i+1<n and grid[i+1][j]==0:
  40. uf.union(get_index(i,j), get_index(i+1,j) )
  41. if j+1<m and grid[i][j+1]==0:
  42. uf.union(get_index(i,j), get_index(i,j+1) )
  43. if i>0 and grid[i-1][j]==0:
  44. uf.union(get_index(i,j), get_index(i-1,j) )
  45. if j>0 and grid[i][j-1]==0:
  46. uf.union(get_index(i,j), get_index(i,j-1) )
  47. if grid[i][j]==1:
  48. uf.union(get_index(i,j), n*m )
  49. for i,p in enumerate(uf.parent):
  50. uf.parent[i]=uf.find(uf.parent[i])
  51. #print(uf.parent)
  52. return len(set(uf.parent))-1