AlgoVerse

شکافتن و پیوند درخت جستجوی دودویی

درخت جستجو را با کلید k به دو درخت (کوچک‌تر و بزرگ‌تر از k) می‌شکافد و بعد دو درخت را دوباره پیوند می‌دهد؛ هر دو O(h). شکافتن و سپس پیوند همان حذف k است.

SPLIT(t, k) درخت جستجو را به سه بخش تقسیم می‌کند: درخت small با کلیدهای کمتر از k، گره‌ی k (اگر باشد)، و درخت big با کلیدهای بیشتر از k. فقط در مسیر جست‌وجوی k پایین می‌رویم:

ادامه‌ی توضیح ↓
ورودی:
A 1×10 · k=40خروجی PDF

درخت ۱۰ کلیدی با k = ۴۰؛ مسیر جست‌وجوی ۴۰ درخت را به دو نیم می‌کند، و بعد بزرگ‌ترین کلید small، یعنی ۳۵، ریشه‌ی درخت پیوندخورده می‌شود.

  1. class Node:
  2. def __init__(self, key):
  3. self.key = key
  4. self.left = None
  5. self.right = None
  6. def split_join(A, k):
  7. root = build(A[0])
  8. small, mid, big = split(root, k)
  9. found = mid is not None
  10. root = None
  11. root = join(small, big)
  12. small = None
  13. big = None
  14. keys = []
  15. inorder(root, keys)
  16. return keys
  17. def split(t, k):
  18. # Returns the keys < k, the node with key k (or None), and the keys > k, as three search trees.
  19. if t is None:
  20. return None, None, None
  21. if k < t.key:
  22. s, m, b = split(t.left, k)
  23. t.left = b
  24. return s, m, t
  25. if k > t.key:
  26. s, m, b = split(t.right, k)
  27. t.right = s
  28. return t, m, b
  29. s, b = t.left, t.right
  30. t.left = None
  31. t.right = None
  32. return s, t, b
  33. def join(small, big):
  34. # Every key in small is below every key in big. The largest key of small becomes the new root.
  35. if small is None:
  36. return big
  37. parent = None
  38. m = small
  39. while m.right is not None:
  40. parent = m
  41. m = m.right
  42. if parent is None:
  43. small = m.left
  44. else:
  45. parent.right = m.left
  46. m.left = small
  47. m.right = big
  48. return m
  49. def inorder(t, keys):
  50. if t is not None:
  51. inorder(t.left, keys)
  52. keys.append(t.key)
  53. inorder(t.right, keys)
  54. def build(keys):
  55. root = None
  56. for key in keys:
  57. node = Node(key)
  58. if root is None:
  59. root = node
  60. continue
  61. p = root
  62. while True:
  63. if key < p.key:
  64. if p.left is None:
  65. p.left = node
  66. break
  67. p = p.left
  68. else:
  69. if p.right is None:
  70. p.right = node
  71. break
  72. p = p.right
  73. return root

ناوردا: در شکافتن، هر گره‌ی مسیر جست‌وجوی k با یکی از زیردرخت‌هایش دست‌نخورده به small یا big می‌رود؛ همه‌ی کلیدهای small کمتر از k و همه‌ی کلیدهای big بیشتر از k اند.

شروع اجرا

root

∅

small

∅

big

∅

درخت فراخوانی‌های بازگشتی

نارنجی پر = فراخوانی در حال اجرا · حاشیه‌ی نارنجی = هنوز روی پشته · خاکستری = تمام شده

متغیرهای محلی

k40

نارنجی = در همین گام عوض شد

A list

[50, 30, 70, 20, 40, 60, 80, 35, 45, 65]

Call stack

  1. split_join()خط 8
گام ۱ از ۲۸۰
خط سیاه = split_done, joined · خط نارنجی = گام کلیدیbuilt: 0/1 · found: 0/1 · keep_left: 0/1 · keep_right: 0/1 · split_done: 0/1 · find_max: 0/1 · unlink_max: 0/1 · new_root: 0/1 · joined: 0/1
    توضیح

    شکافتن

    SPLIT(t, k) درخت جستجو را به سه بخش تقسیم می‌کند: درخت small با کلیدهای کمتر از k، گره‌ی k (اگر باشد)، و درخت big با کلیدهای بیشتر از k. فقط در مسیر جست‌وجوی k پایین می‌رویم:

    • اگر k < t.key: گره‌ی t با زیردرخت راستش به big می‌رود و شکافتن در زیردرخت چپ ادامه دارد.
    • اگر k > t.key: برعکس، t با زیردرخت چپش به small می‌رود.

    پیوند

    JOIN(small, big) دو درخت را که همه‌ی کلیدهای اولی از دومی کوچک‌ترند یکی می‌کند: بزرگ‌ترین گره‌ی small را جدا می‌کنیم و ریشه‌ی تازه می‌کنیم، با small در چپ و big در راست. اگر یک کلید میانی m هم داشته باشیم (پیوند سه‌طرفه) کار ساده‌تر است: m ریشه می‌شود.

    هزینه

    هر دو فقط یک مسیر از ریشه تا برگ را می‌روند: O(h). این دو عمل پایه‌ی درخت‌های treap و splay و پیاده‌سازی مجموعه‌ها با اجتماع و اشتراک‌اند.

    ورودی

    A ترتیب درج کلیدها و k کلید شکافتن است.