AlgoVerse

دور اویلری (هیرهولتسر)

دوری پیدا می‌کند که از هر یال دقیقاً یک بار می‌گذرد؛ هر جا گیر کرد، یک دور فرعی را در مسیر جا می‌دهد.

دور اویلری گذرگاه بسته‌ای است که از هر یال دقیقاً یک بار می‌گذرد (مسئله‌ی پل‌های کونیگسبرگ). اویلر نشان داد در گراف همبند چنین دوری هست اگر و فقط اگر درجه‌ی همه‌ی گره‌ها زوج باشد.

ادامه‌ی توضیح ↓
ورودی:
V=6 · E=12 · src=Aخروجی PDF

هشت‌وجهی: شش گره که درجه‌ی همه ۴ است؛ گذرگاه اول زود بسته می‌شود و بقیه به‌صورت دورهای فرعی جا داده می‌شوند.

  1. def euler_circuit(graph, src):
  2. for v in graph:
  3. if len(graph[v]) % 2 == 1:
  4. return None
  5. remaining = {v: list(graph[v]) for v in graph}
  6. edge_count = sum(len(graph[v]) for v in graph) // 2
  7. used = []
  8. stack = [src]
  9. circuit = []
  10. while stack:
  11. u = stack[-1]
  12. if remaining[u]:
  13. v, w = remaining[u].pop(0)
  14. remaining[v].remove((u, w))
  15. used.append((u, v, w))
  16. stack.append(v)
  17. else:
  18. circuit.append(stack.pop())
  19. if len(used) < edge_count:
  20. return None
  21. circuit.reverse()
  22. return circuit

ناوردا: پشته همیشه یک گذرگاه از s است و circuit (برعکس) انتهای دور نهایی است؛ هر گره‌ای که گیر کند دیگر یال آزاد ندارد.

شروع اجرا

ABCDEF
  • گره‌ی جاری (u)
  • یال‌های پیموده‌شده
  • یالِ در حال بررسی

پشته stack

خالی

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

src'A'

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

graph dict
  • 'A': [('B', 1), ('C', 1), ('D', 1), ('E', 1)]
  • 'B': [('A', 1), ('F', 1), ('C', 1), ('E', 1)]
  • 'C': [('A', 1), ('F', 1), ('B', 1), ('D', 1)]
  • 'D': [('A', 1), ('F', 1), ('C', 1), ('E', 1)]
  • 'E': [('A', 1), ('F', 1), ('D', 1), ('B', 1)]
  • 'F': [('B', 1), ('C', 1), ('D', 1), ('E', 1)]

Call stack

  1. euler_circuit()خط 1
گام ۱ از ۱۵۹
خط سیاه = top · خط نارنجی = گام کلیدیdegree: 0/6 · top: 0/25 · walk: 0/12 · stuck: 0/13 · all_used: 0/1
    توضیح

    ایده

    دور اویلری گذرگاه بسته‌ای است که از هر یال دقیقاً یک بار می‌گذرد (مسئله‌ی پل‌های کونیگسبرگ). اویلر نشان داد در گراف همبند چنین دوری هست اگر و فقط اگر درجه‌ی همه‌ی گره‌ها زوج باشد.

    الگوریتم هیرهولتسر

    1. از مبدأ راه بیفت و هر بار از یک یال آزاد بگذر (و آن را مصرف کن). گره‌ها را روی پشته بگذار.
    2. چون درجه‌ها زوج‌اند، فقط در گره‌ای گیر می‌کنی که دور را بسته باشد.
    3. وقتی گره‌ی بالای پشته یال آزاد ندارد، آن را از پشته بردار و به circuit اضافه کن. اگر گره‌ی زیرین هنوز یال آزاد دارد، از آنجا یک دور فرعی شروع می‌شود که خودبه‌خود در جای درست جا می‌گیرد.

    در پایان، circuit برعکس‌شده همان دور اویلری است.

    هزینه

    هر یال یک بار پیموده و هر گره‌ی پشته یک بار برداشته می‌شود: O(V + E).

    در trace به چه چیزی نگاه کنی

    • یال‌های رنگی پیموده شده‌اند؛ پشته زیر گراف است.
    • ورودی «پاپیون» را ببین: لحظه‌ای که A گیر می‌کند و دور فرعی از C شروع می‌شود.