并查集是一种简洁而优雅的数据结构,主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:

  • 合并(Union):把两个不相交的集合合并为一个集合(建立连接)。
  • 查询(Find):查询两个元素是否在同一个集合中。
  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/280095/1616379660784-2310d34f-bb87-41ff-a081-e573864d76a9.png#align=left&display=inline&height=241&margin=%5Bobject%20Object%5D&name=image.png&originHeight=481&originWidth=796&size=91077&status=done&style=none&width=398)

Union Quick版本

  1. class UnionFind:
  2. """
  3. Quick Find
  4. id 0 1 2 3 4 5 6 7 8
  5. 关系 4 5 有联系
  6. 存储 0 1 2 3 5 5 6 7 8
  7. """
  8. def __init__(self, n: int):
  9. # 存储关系 初始值 - 当前数据只与本身有关系即存储所属集合的编号
  10. self.id = [i for i in range(n)]
  11. self.count = n
  12. # 获取集合数量
  13. def size(self):
  14. return self.count
  15. # 查找元素对应的集合编号
  16. def find(self, p: int) -> int:
  17. assert self.count > p >= 0, "p invalid"
  18. return self.id[p]
  19. # 是哦福存在连接 O(1)
  20. def is_connected(self, p: int, q: int) -> bool:
  21. return self.find(p) == self.find(q)
  22. # 合并元素p和元素q所对应的集合 O(n)
  23. def union(self, p: int, q: int):
  24. pid = self.find(p)
  25. qid = self.find(q)
  26. if pid == qid:
  27. return
  28. for i in range(self.count):
  29. # 如果元素id[i]所对应的集合id和p的相同,
  30. # 即找到了和p相关集合的值 和q建立关系
  31. if self.id[i] == pid:
  32. self.id[i] = qid

Quick Union

  1. class UnionFind:
  2. """
  3. Quick Union
  4. id 0 1 2 3 4 5 6 7 8 9
  5. 关系
  6. 0 1 8
  7. ^ ^ ^ ^ ^
  8. | | \ | \
  9. 5 2 7 3 9
  10. ^ ^
  11. | |
  12. 6 4
  13. 数组具体存储
  14. 0 1 1 8 3 0 5 1 8 8
  15. 问题 union(4 9) 节点层数太高
  16. 9
  17. ^
  18. 8
  19. ^
  20. |
  21. 3
  22. ^
  23. 4
  24. union(9 4)
  25. 8
  26. ^ ^
  27. | \
  28. 3 9
  29. ^
  30. 4
  31. find 指向共同的父节点即算是有关联
  32. """
  33. def __init__(self, n: int):
  34. self.id = [i for i in range(n)]
  35. self.count = n
  36. # 获取集合数量
  37. def size(self):
  38. return self.count
  39. # 时间复杂度O(h) h为树的高度
  40. def find(self, p: int) -> int:
  41. # 找不到则指向自己
  42. assert self.count > p >= 0, "p invalid"
  43. # 直到指向自己(即到根位置)
  44. while p != self.id[p]:
  45. # 表明已经指向其他根 从下往上查找根
  46. p = self.id[p]
  47. return p
  48. def is_connected(self, p: int, q: int) -> bool:
  49. return self.find(p) == self.find(q)
  50. def union(self, p: int, q: int):
  51. # 始终是 左指向右面的值
  52. p_root = self.find(p)
  53. q_root = self.find(q)
  54. if p_root == q_root:
  55. return
  56. # p元素的根指向变成q元素的根
  57. self.id[p_root] = q_root

基于Size(当前树的节点)优化

  1. class UnionFind:
  2. """ 并差集优化 基于size的优化
  3. id 0 1 2 3 4 5 6 7 8 9
  4. size 1 1 1 1 1 1 1 1 1 1 (存储高度)
  5. 第一步:
  6. 模拟 union(4,3) -- p q
  7. 4的根4 size 1
  8. 3的根3 size 1
  9. 4
  10. ^
  11. |
  12. 3
  13. index 0 1 2 3 4 5 6 7 8 9
  14. id 0 1 2 4 4 5 6 7 8 9
  15. size 1 1 1 1 2 1 1 1 1 1
  16. 模拟 union(3,8) -- p q
  17. 3的根为4
  18. 8的根为8
  19. 4 4 | 4
  20. ^ ^ | not ^ ^
  21. | | | | \
  22. 3 8 | 3 8
  23. index 0 1 2 3 4 5 6 7 8 9
  24. id 0 1 2 4 4 5 6 7 4 9
  25. size 1 1 1 1 3 1 1 1 1 1
  26. """
  27. def __init__(self, n: int):
  28. self.id = [i for i in range(n)]
  29. self.count = n
  30. self.size = [1 for _ in range(n)] # size[i] 表示以i为根的集合中的元素个数
  31. # 获取集合数量
  32. def size(self):
  33. return self.count
  34. def find(self, p: int) -> int:
  35. # 找不到则指向自己
  36. assert self.count > p >= 0, "p invalid"
  37. while p != self.id[p]:
  38. p = self.id[p]
  39. return p
  40. def is_connected(self, p: int, q: int) -> bool:
  41. return self.find(p) == self.find(q)
  42. def union(self, p: int, q: int):
  43. p_root = self.find(p)
  44. q_root = self.find(q)
  45. if p_root == q_root:
  46. return
  47. # 判断当前父节点被关联的次数
  48. if self.size[p_root] < self.size[q_root]:
  49. self.id[p_root] = q_root
  50. self.size[q_root] = self.size[p_root]
  51. else:
  52. self.id[q_root] = p_root
  53. self.size[p_root] = self.size[q_root]

基于Rank(树的高度)

  1. class UnionFind:
  2. """
  3. 并查集优化 基于rank的优化 (树的高度)
  4. 问题
  5. 当前状态 8
  6. -----------------> 7 ^
  7. | | | | | |
  8. | | | | | 3
  9. | | | | | ^
  10. | | | | | |
  11. 0 1 2 5 6 4
  12. 按照UinonFind3操作 union(4,2)
  13. -----------------> 7
  14. | | | | | \
  15. | | | | | \
  16. 0 1 2 5 6 8
  17. ^
  18. |
  19. 3
  20. ^
  21. |
  22. 4
  23. 共 4 层
  24. 优化方案
  25. > 8
  26. / |
  27. / ^
  28. -----------------> 7 |
  29. | | | | | 3
  30. | | | | | |
  31. 0 1 2 5 6 4
  32. 共 3 层
  33. """
  34. def __init__(self, n: int):
  35. self.id = [i for i in range(n)]
  36. self.count = n
  37. self.rank = [1 for _ in range(n)] # rank[i] 表示以i为根的集合的高度
  38. # 获取集合数量
  39. def size(self):
  40. return self.count
  41. def find(self, p: int) -> int:
  42. # 找不到则指向自己
  43. assert self.count > p >= 0, "p invalid"
  44. while p != self.id[p]:
  45. p = self.id[p]
  46. return p
  47. def is_connected(self, p: int, q: int) -> bool:
  48. return self.find(p) == self.find(q)
  49. def union(self, p: int, q: int):
  50. p_root = self.find(p)
  51. q_root = self.find(q)
  52. if p_root == q_root:
  53. return
  54. # 判断当前父节点的高度
  55. if self.rank[p_root] < self.rank[q_root]:
  56. self.id[p_root] = q_root
  57. elif self.rank[q_root] < self.rank[p_root]:
  58. self.id[q_root] = p_root
  59. else:
  60. self.id[q_root] = p_root
  61. self.rank[p_root] += 1

路径压缩

  1. class UnionFind:
  2. """
  3. 并查集优化 路径压缩
  4. 问题
  5. 当前状态
  6. 1
  7. /
  8. 2
  9. /
  10. 3
  11. /
  12. 5
  13. /
  14. 9
  15. 在查找过程中进行路径压缩
  16. step 1
  17. 1
  18. /
  19. 2
  20. /
  21. 3
  22. / \
  23. 5 9
  24. step 2
  25. 1
  26. / \
  27. 2 3
  28. / \
  29. 5 9
  30. """
  31. def __init__(self, n: int):
  32. self.id = [i for i in range(n)]
  33. self.count = n
  34. self.rank = [1 for _ in range(n)] # rank[i] 表示以i为根的集合的高度
  35. # 获取集合数量
  36. def size(self):
  37. return self.count
  38. def find(self, p: int) -> int:
  39. # 找不到则指向自己
  40. assert self.count > p >= 0, "p invalid"
  41. while p != self.id[p]:
  42. self.id[p] = self.id[self.id[p]]
  43. p = self.id[p]
  44. return p
  45. def is_connected(self, p: int, q: int) -> bool:
  46. return self.find(p) == self.find(q)
  47. def union(self, p: int, q: int):
  48. p_root = self.find(p)
  49. q_root = self.find(q)
  50. if p_root == q_root:
  51. return
  52. # 判断当前父节点的高度
  53. if self.rank[p_root] < self.rank[q_root]:
  54. self.id[p_root] = q_root
  55. elif self.rank[q_root] < self.rank[p_root]:
  56. self.id[q_root] = p_root
  57. else:
  58. self.id[q_root] = p_root
  59. self.rank[p_root] += 1

路径压缩2

  1. class UnionFind:
  2. """
  3. 并查集优化 路径压缩
  4. 问题
  5. 当前状态
  6. 1
  7. /
  8. 2
  9. /
  10. 3
  11. /
  12. 5
  13. /
  14. 9
  15. 在查找过程中进行路径压缩
  16. step 2
  17. 1
  18. /||\
  19. / / | \
  20. 2 3 5 9
  21. """
  22. def __init__(self, n: int):
  23. self.id = [i for i in range(n)]
  24. self.count = n
  25. self.rank = [1 for _ in range(n)] # rank[i] 表示以i为根的集合的高度
  26. # 获取集合数量
  27. def size(self):
  28. return self.count
  29. def find(self, p: int) -> int:
  30. # 找不到则指向自己
  31. assert self.count > p >= 0, "p invalid"
  32. if p != self.id[p]:
  33. self.id[p] = self.find(self.id[p])
  34. return self.id[p]
  35. def is_connected(self, p: int, q: int) -> bool:
  36. return self.find(p) == self.find(q)
  37. def union(self, p: int, q: int):
  38. p_root = self.find(p)
  39. q_root = self.find(q)
  40. if p_root == q_root:
  41. return
  42. # 判断当前父节点的高度
  43. if self.rank[p_root] < self.rank[q_root]:
  44. self.id[p_root] = q_root
  45. elif self.rank[q_root] < self.rank[p_root]:
  46. self.id[q_root] = p_root
  47. else:
  48. self.id[q_root] = p_root
  49. self.rank[p_root] += 1