1
0

00-从排好队到会分身的树.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/env python3
  2. """课前零食:可操作地追踪一棵按值 FHQ Treap。"""
  3. from __future__ import annotations
  4. from dataclasses import dataclass
  5. import random
  6. RNG = random.Random(20260903)
  7. @dataclass
  8. class Node:
  9. key: int
  10. priority: int
  11. count: int = 1
  12. size: int = 1
  13. left: Node | None = None
  14. right: Node | None = None
  15. def node_size(node: Node | None) -> int:
  16. return node.size if node else 0
  17. def pull(node: Node | None, log: list[str]) -> None:
  18. if node:
  19. node.size = node_size(node.left) + node.count + node_size(node.right)
  20. log.append(f"pull({node.key}):size={node.size}")
  21. def split(node: Node | None, key: int, log: list[str]) -> tuple[Node | None, Node | None]:
  22. """返回所有键 <= key 的树,以及所有键 > key 的树。"""
  23. if node is None:
  24. return None, None
  25. if node.key <= key:
  26. log.append(f"split({node.key}, {key}):{node.key} 放左半,继续切右子树")
  27. middle, right = split(node.right, key, log)
  28. node.right = middle
  29. pull(node, log)
  30. return node, right
  31. log.append(f"split({node.key}, {key}):{node.key} 放右半,继续切左子树")
  32. left, middle = split(node.left, key, log)
  33. node.left = middle
  34. pull(node, log)
  35. return left, node
  36. def merge(left: Node | None, right: Node | None, log: list[str]) -> Node | None:
  37. """前提:left 的所有键 <= right 的所有键。"""
  38. if left is None:
  39. return right
  40. if right is None:
  41. return left
  42. if left.priority > right.priority:
  43. log.append(f"merge:{left.key}(p={left.priority}) 优先级更高,做根")
  44. left.right = merge(left.right, right, log)
  45. pull(left, log)
  46. return left
  47. log.append(f"merge:{right.key}(p={right.priority}) 优先级更高,做根")
  48. right.left = merge(left, right.left, log)
  49. pull(right, log)
  50. return right
  51. def insert(root: Node | None, key: int) -> tuple[Node | None, list[str]]:
  52. log: list[str] = []
  53. less, at_least = split(root, key - 1, log)
  54. equal, greater = split(at_least, key, log)
  55. if equal:
  56. equal.count += 1
  57. pull(equal, log)
  58. log.append(f"键 {key} 已存在:count 增为 {equal.count}")
  59. else:
  60. equal = Node(key=key, priority=RNG.randrange(1, 100))
  61. log.append(f"新建 {key},随机优先级为 {equal.priority}")
  62. return merge(less, merge(equal, greater, log), log), log
  63. def erase_one(root: Node | None, key: int) -> tuple[Node | None, list[str]]:
  64. log: list[str] = []
  65. less, at_least = split(root, key - 1, log)
  66. equal, greater = split(at_least, key, log)
  67. if equal is None:
  68. log.append(f"没有键 {key},原树接回去。")
  69. elif equal.count > 1:
  70. equal.count -= 1
  71. pull(equal, log)
  72. log.append(f"键 {key} 的 count 降为 {equal.count}")
  73. else:
  74. log.append(f"删除唯一的键 {key},把它的左右孩子合并。")
  75. equal = merge(equal.left, equal.right, log)
  76. return merge(less, merge(equal, greater, log), log), log
  77. def rank(root: Node | None, key: int) -> int:
  78. answer = 1
  79. while root:
  80. if key <= root.key:
  81. root = root.left
  82. else:
  83. answer += node_size(root.left) + root.count
  84. root = root.right
  85. return answer
  86. def kth(root: Node | None, k: int) -> int | None:
  87. while root:
  88. left_count = node_size(root.left)
  89. if k <= left_count:
  90. root = root.left
  91. elif k <= left_count + root.count:
  92. return root.key
  93. else:
  94. k -= left_count + root.count
  95. root = root.right
  96. return None
  97. def inorder(root: Node | None) -> list[int]:
  98. if root is None:
  99. return []
  100. return inorder(root.left) + [root.key] * root.count + inorder(root.right)
  101. def show_tree(root: Node | None, prefix: str = "", side: str = "根") -> None:
  102. if root is None:
  103. return
  104. show_tree(root.right, prefix + " ", "右")
  105. print(f"{prefix}{side}: {root.key} [p={root.priority}, cnt={root.count}, size={root.size}]")
  106. show_tree(root.left, prefix + " ", "左")
  107. def print_log(log: list[str]) -> None:
  108. for item in log:
  109. print(" ", item)
  110. def warmup() -> None:
  111. """让实验从数组/BST 的失败开始,而不是直接倾倒递归日志。"""
  112. print("\n=== 任务 0:先当人脑数据结构 ===")
  113. print("集合是 [4, 2, 7, 5, 2]。请先在纸上排成 [2, 2, 4, 5, 7]。")
  114. try:
  115. input("若插入 6,有序数组中哪几个元素要右移?写下预测后按回车: ")
  116. except EOFError:
  117. pass
  118. print("答案检查:6 放在 5 与 7 之间,至少要把 7 向右搬一格。")
  119. print("再想一想递增插入 1,2,3,4 的普通 BST:它会变成链。")
  120. try:
  121. input("四张优先级卡 2/35、4/60、5/83、7/20 中,谁应做 Treap 根?按回车核对: ")
  122. except EOFError:
  123. pass
  124. print("答案检查:5 的优先级 83 最大;键值左右关系仍由 key 决定。")
  125. print("接下来的日志只验证你刚搭出的框架:切范围、改目标块、合并、pull。")
  126. def main() -> None:
  127. root: Node | None = None
  128. print("FHQ Treap 小实验室:固定随机种子,方便复盘。")
  129. warmup()
  130. for value in (4, 2, 7, 5, 2):
  131. root, log = insert(root, value)
  132. print(f"\n插入 {value}:")
  133. print_log(log)
  134. print("\n初始树:")
  135. show_tree(root)
  136. print("中序(应有序):", inorder(root))
  137. print("命令:+ x;- x;rank x;k k;show;q。直接回车退出。")
  138. while True:
  139. try:
  140. raw = input("\n> ").strip()
  141. except EOFError:
  142. raw = "q"
  143. if not raw or raw in {"q", "quit", "exit"}:
  144. break
  145. parts = raw.split()
  146. try:
  147. if parts[0] == "+" and len(parts) == 2:
  148. root, log = insert(root, int(parts[1]))
  149. print_log(log)
  150. elif parts[0] == "-" and len(parts) == 2:
  151. root, log = erase_one(root, int(parts[1]))
  152. print_log(log)
  153. elif parts[0] == "rank" and len(parts) == 2:
  154. print(f"rank({parts[1]}) = {rank(root, int(parts[1]))}")
  155. elif parts[0] == "k" and len(parts) == 2:
  156. value = kth(root, int(parts[1]))
  157. print("第 k 小:" if value is not None else "k 超出元素总数。", value if value is not None else "")
  158. elif parts[0] == "show":
  159. show_tree(root)
  160. print("中序:", inorder(root))
  161. else:
  162. raise ValueError("命令格式不对")
  163. except ValueError as error:
  164. print("小提示:", error)
  165. if __name__ == "__main__":
  166. main()