AlgoVerse

درخت برنده (و بازنده) برای ادغام k-راهه

k فهرست مرتب را با یک درخت مسابقه ادغام می‌کند: ریشه کوچک‌ترین سر فهرست‌هاست و بعد از هر خروجی، فقط بازی‌های یک مسیر برگ تا ریشه دوباره انجام می‌شود؛ O(log k) برای هر عضو.

k فهرست مرتب داریم (مثلاً بخش‌های مرتب‌شده‌ی یک فایل بزرگ در مرتب‌سازی خارجی) و می‌خواهیم آن‌ها را در یک فهرست مرتب ادغام کنیم. هر بار باید کوچک‌ترین سر فهرست‌ها را پیدا کنیم؛ مقایسه‌ی همه O(k) است.

ادامه‌ی توضیح ↓
ورودی:
R 4×3خروجی PDF

چهار فهرست مرتب سه‌تایی؛ هر خروجی فقط دو بازی (log₂ ۴) را دوباره انجام می‌دهد و نتیجه ۱۲ عضو مرتب است.

  1. class Node:
  2. def __init__(self, key, run):
  3. self.key = key
  4. self.run = run # the run this key came from
  5. self.left = None
  6. self.right = None
  7. self.parent = None
  8. def k_way_merge(R):
  9. # R: k sorted runs, one per row. The leaves are the runs' current heads; each inner node holds the smaller child.
  10. k = len(R)
  11. size = 1
  12. while size < k:
  13. size = size * 2
  14. pos = [0] * size
  15. leaves = []
  16. for r in range(size):
  17. key = float("inf")
  18. if r < k and R[r]:
  19. key = R[r][0]
  20. leaves.append(Node(key, r))
  21. level = leaves
  22. while len(level) > 1:
  23. up = []
  24. for i in range(0, len(level), 2):
  25. a = level[i]
  26. b = level[i + 1]
  27. w = a if a.key <= b.key else b
  28. node = Node(w.key, w.run)
  29. node.left = a
  30. node.right = b
  31. a.parent = node
  32. b.parent = node
  33. up.append(node)
  34. level = up
  35. root = level[0]
  36. out = []
  37. while root.key != float("inf"):
  38. out.append(root.key)
  39. r = root.run
  40. pos[r] = pos[r] + 1
  41. leaf = leaves[r]
  42. leaf.key = R[r][pos[r]] if pos[r] < len(R[r]) else float("inf")
  43. node = leaf.parent
  44. while node is not None:
  45. w = node.left if node.left.key <= node.right.key else node.right
  46. node.key = w.key
  47. node.run = w.run
  48. node = node.parent
  49. return out

ناوردا: هر گره‌ی داخلی کوچک‌ترین کلید برگ‌های زیرش را دارد؛ پس ریشه کوچک‌ترین عضو باقی‌مانده است.

شروع اجرا

∅

out—

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

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

R list

[10, 15, 16][9, 20, 38][20, 20, 30][6, 15, 25]

Call stack

  1. k_way_merge()خط 10
گام ۱ از ۳۳۳
خط سیاه = output · خط نارنجی = گام کلیدیbuilt: 0/1 · output: 0/12 · next_key: 0/12 · replay: 0/24
    توضیح

    مسئله

    k فهرست مرتب داریم (مثلاً بخش‌های مرتب‌شده‌ی یک فایل بزرگ در مرتب‌سازی خارجی) و می‌خواهیم آن‌ها را در یک فهرست مرتب ادغام کنیم. هر بار باید کوچک‌ترین سر فهرست‌ها را پیدا کنیم؛ مقایسه‌ی همه O(k) است.

    درخت برنده

    یک درخت مسابقه می‌سازیم: برگ‌ها سر فهرست‌ها هستند و هر گره‌ی داخلی برنده (کلید کوچک‌تر) بازی دو فرزندش است. ریشه برنده‌ی کل است. بعد از خروجی دادن ریشه، فقط برگِ فهرست برنده عوض می‌شود، پس فقط بازی‌های مسیر آن برگ تا ریشه دوباره انجام می‌شوند: ⌈log₂ k⌉ مقایسه.

    درخت بازنده

    در درخت برنده، برای بازی دوباره در هر گره باید سراغ هر دو فرزند برویم. در درخت بازنده هر گره‌ی داخلی بازنده‌ی بازی‌اش را نگه می‌دارد و برنده‌ی کل جداگانه نگه داشته می‌شود. در بازی دوباره، کلید تازه فقط با بازنده‌ی ذخیره‌شده در هر گره‌ی مسیر مقایسه می‌شود: کمتر به حافظه دسترسی دارد و در مرتب‌سازی خارجی ترجیح داده می‌شود.

    ورودی

    هر سطر R یک فهرست مرتب است. وقتی فهرستی تمام شد، برگش ∞ می‌شود.