AlgoVerse

جستجوی اول عمق (DFS)

از مبدأ تا جایی که می‌شود در یک مسیر جلو می‌رود و وقتی به بن‌بست رسید، برمی‌گردد و راه دیگری را امتحان می‌کند.

جستجوی اول عمق از مبدأ تا جایی که می‌تواند در یک مسیر جلو می‌رود. وقتی گره‌ی جاری هیچ همسایه‌ی دیده‌نشده‌ای نداشت (بن‌بست)، کارش تمام است و DFS یک قدم برمی‌گردد تا از گره‌ی قبلی راه دیگری را امتحان کند.

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

گراف جهت‌دار شش‌گره‌ای با یک دور؛ DFS از U عمیق می‌رود، به بن‌بست می‌رسد و برمی‌گردد.

  1. def dfs(graph, src):
  2. seen = set()
  3. parent = {v: None for v in graph}
  4. order = []
  5. visit(graph, src, seen, parent, order)
  6. return order, parent
  7. def visit(graph, u, seen, parent, order):
  8. seen.add(u)
  9. order.append(u)
  10. for v, _ in graph[u]:
  11. if v not in seen:
  12. parent[v] = u
  13. visit(graph, v, seen, parent, order)
  14. return

ناوردا: گره‌هایی که روی پشته‌ی فراخوانی‌اند، مسیری از مبدأ تا گره‌ی جاری در درخت DFS می‌سازند.

شروع اجرا

UVWXYZ
  • دیده‌شده
  • گره‌ی جاری (u)
  • درخت پیمایش (از parent)
  • یالِ در حال بررسی

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

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

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

src'U'

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

graph dict
  • 'U': [('V', 1), ('X', 1)]
  • 'V': [('Y', 1)]
  • 'W': [('Y', 1), ('Z', 1)]
  • 'X': [('V', 1)]
  • 'Y': [('X', 1)]
  • 'Z': []

Call stack

  1. dfs()خط 1
گام ۱ از ۴۲
خط سیاه = discover · خط نارنجی = گام کلیدیdiscover: 0/4 · check: 0/5 · descend: 0/3 · finish: 0/4

    رشد تعداد عملیات با اندازه‌ی ورودی

    میانگین ۵ گراف تصادفی برای هر n (با seed ثابت). نشانگر توخالی = ورودی فعلی.

    • check
    • discover
    • ورودی فعلی (n=6)
    0204060481216202428n = تعداد گره‌هاcheckdiscover
    نمایش به شکل جدول
    ncheckdiscover
    45.24
    813.88
    1220.812
    162816
    2035.420
    2437.224
    2847.228
    توضیح

    ایده

    جستجوی اول عمق از مبدأ تا جایی که می‌تواند در یک مسیر جلو می‌رود. وقتی گره‌ی جاری هیچ همسایه‌ی دیده‌نشده‌ای نداشت (بن‌بست)، کارش تمام است و DFS یک قدم برمی‌گردد تا از گره‌ی قبلی راه دیگری را امتحان کند.

    این برگشتن را بازگشت (recursion) خودبه‌خود انجام می‌دهد: هر visit(u) برای همسایه‌ی تازه visit(v) را صدا می‌زند و وقتی آن تمام شد، کار u ادامه پیدا می‌کند. پس پشته‌ی فراخوانی همان مسیر از مبدأ تا گره‌ی جاری است.

    گام‌ها

    1. u را به seen اضافه کن (کشف شد).
    2. برای هر همسایه‌ی v از u: اگر v دیده نشده، parent[v] = u را ثبت کن و visit(v) را صدا بزن.
    3. وقتی همه‌ی همسایه‌ها بررسی شدند، u تمام شده است؛ برگرد.

    هزینه

    هر گره یک بار کشف می‌شود و هر یال یک بار (در گراف بی‌جهت دو بار) بررسی می‌شود: O(V + E). عمق بازگشت در بدترین حالت V است؛ ورودی «زنجیر» را ببین.

    کاربردها

    DFS پایه‌ی خیلی از الگوریتم‌های گراف است: پیدا کردن دور، مرتب‌سازی توپولوژیک، مؤلفه‌های قویاً همبند و نقاط برش.

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

    • گره‌های سیاه دیده شده‌اند و گره‌ی نارنجی همان u در فراخوانی جاری است.
    • یال‌های آبی درخت DFS‌اند (از parent).
    • «درخت فراخوانی‌های بازگشتی» دقیقاً همان درخت DFS است: ببین پشته کی عمیق می‌شود و کی برمی‌گردد.
    • BFS و DFS را روی یک گراف مقایسه کن: BFS پهن جلو می‌رود و DFS عمیق.