Menu
Coddy logo textTech

DFS (przeszukiwanie w głąb)

Ostatnia aktualizacja

Przeszukiwanie w głąb przegląda graf, schodząc jak najgłębiej wzdłuż każdej gałęzi, zanim się wycofa. Zaczynając od węzła źródłowego, odwiedza sąsiada, potem sąsiada tego sąsiada i tak dalej, aż trafi w ślepy zaułek (węzeł bez nieodwiedzonych sąsiadów); wtedy wraca i próbuje następnej gałęzi. Kliknij odtwarzanie powyżej i zobacz, jak algorytm schodzi jedną ścieżką, dociera do dna i wraca.

DFS naturalnie wyraża się za pomocą stosu: jawnego stosu (jak w tej animacji) albo stosu wywołań w rekurencji. Odwiedza każdy węzeł i każdą krawędź raz, więc działa w czasie O(V + E) dla grafu z V wierzchołkami i E krawędziami.

Złożoność czasowa i pamięciowa

MiaraZłożonośćUwagi
CzasO(V + E)Każdy wierzchołek i krawędź odwiedzane raz
PamięćO(V)Stos + zbiór odwiedzonych, w najgorszym razie wszystkie węzły
PrzechodzenieNajpierw najgłębiejPodąża jedną gałęzią do końca, zanim zajmie się innymi
Struktura danychStos (lub rekurencja)Kolejność LIFO wymusza działanie w głąb

Krok po kroku

KrokCo się dzieje
1Połóż węzeł źródłowy na stosie.
2Zdejmij węzeł; jeśli był już odwiedzony, pomiń go.
3Oznacz go jako odwiedzony i zapamiętaj krawędź, która do niego doprowadziła.
4Połóż na stosie wszystkich jego nieodwiedzonych sąsiadów.
5Powtarzaj, aż stos będzie pusty.

Przykład krok po kroku

Iteracyjne DFS od A w grafie A → [B, C], B → [D], C → [E], z sąsiadami kładzionymi w podanej kolejności (stos LIFO zdejmuje najpierw ostatnio położony):

KrokStos (szczyt po prawej)OdwiedzoneDziałanie
1[A]{}Połóż źródło A.
2[B, C]{A}Zdejmij A, oznacz jako odwiedzony, połóż sąsiadów B, a potem C.
3[B, E]{A, C}Zdejmij C, oznacz jako odwiedzony, połóż sąsiada E.
4[B]{A, C, E}Zdejmij E, oznacz jako odwiedzony, brak sąsiadów.
5[D]{A, C, E, B}Zdejmij B, oznacz jako odwiedzony, połóż sąsiada D.
6[]{A, C, E, B, D}Zdejmij D, oznacz jako odwiedzony, stos pusty: koniec.

Kiedy używać DFS

Używaj, gdyUnikaj, gdy
Potrzebujesz wykrywania cykli, sortowania topologicznego lub znajdowania spójnych składowych.Potrzebujesz najkrótszej ścieżki w grafie bez wag: robi to BFS, a DFS nie.
Graf jest szeroki i płytki, więc stos zużywa znacznie mniej pamięci niż kolejka z wieloma węzłami granicy.Graf jest bardzo głęboki i używasz rekurencji: stos wywołań może się przepełnić.
Chcesz wyczerpująco przeglądać ścieżki, jak przy rozwiązywaniu labiryntów lub backtrackingu.Potrzebujesz węzłów odkrywanych w kolejności odległości od źródła.
Sprawdzasz osiągalność lub to, czy istnieje ścieżka między dwoma węzłami.Musisz zagwarantować minimalną liczbę krawędzi na znalezionej ścieżce.

Depth-First Search: kod

Przejrzysta, gotowa do uruchomienia implementacja algorytmu Depth-First Search w językach: Python, JavaScript, Java, C++, C. Wybierz język, skopiuj kod albo otwórz go od razu w edytorze online Coddy.

Depth-First Search: kod (Python)

Python
1def dfs(graph, node, visited):2    visited.add(node)3    order = [node]4    for neighbor in graph[node]:5        if neighbor not in visited:6            order.extend(dfs(graph, neighbor, visited))7    return order8
9
10graph = {11    "A": ["B", "C"],12    "B": ["D", "E"],13    "C": ["F"],14    "D": [],15    "E": ["F"],16    "F": [],17}18
19order = dfs(graph, "A", set())20print("DFS order:", " -> ".join(order))
Uruchom ten kod w edytorze Python online

DFS (przeszukiwanie w głąb): najczęstsze pytania

Jaka jest złożoność czasowa DFS?
DFS działa w czasie O(V + E), gdzie V to liczba wierzchołków, a E liczba krawędzi, bo każdy wierzchołek odwiedza raz i każdą krawędź sprawdza raz. Zużywa O(V) pamięci na stos i zbiór odwiedzonych.
Czym różni się DFS od BFS?
DFS używa stosu i schodzi głęboko w jedną gałąź, zanim się wycofa, a BFS używa kolejki i przegląda graf poziom po poziomie, zaczynając od najbliższych węzłów. BFS znajduje najkrótszą ścieżkę w grafie bez wag; DFS nie, ale zużywa mniej pamięci w szerokich grafach i naturalnie nadaje się do zadań takich jak wykrywanie cykli czy sortowanie topologiczne.
Czy DFS jest rekurencyjne czy iteracyjne?
Obie wersje działają. Wersja rekurencyjna niejawnie używa stosu wywołań programu i jest bardzo zwięzła; wersja iteracyjna używa jawnego stosu (jak w tej animacji) i unika przepełnienia stosu przy głębokiej rekurencji w dużych grafach.
Kiedy użyć DFS zamiast BFS?
Używaj DFS, gdy musisz przeglądać całe ścieżki: przy wykrywaniu cykli, sortowaniu topologicznym, szukaniu spójnych składowych lub backtrackingu, na przykład przy rozwiązywaniu labiryntu. DFS zużywa też mniej pamięci w szerokich grafach, bo stos przechowuje naraz granicę tylko jednej gałęzi. Sięgnij po BFS, gdy potrzebujesz najkrótszej ścieżki w grafie bez wag albo węzłów w kolejności odległości od źródła.
Czy DFS znajduje najkrótszą ścieżkę?
Nie w niezawodny sposób. DFS zwraca pierwszą znalezioną ścieżkę, która często nie ma najmniejszej liczby krawędzi, bo algorytm schodzi w głąb zamiast równomiernie rozszerzać się na zewnątrz. Do najkrótszych ścieżek w grafie bez wag użyj BFS, a w grafach ważonych algorytmu Dijkstry.
Dlaczego DFS potrzebuje zbioru odwiedzonych węzłów?
Bez zbioru odwiedzonych DFS będzie ponownie odwiedzać węzły osiągalne wieloma ścieżkami, a w grafie z cyklami zapętli się w nieskończoność. Oznaczanie węzła jako odwiedzonego przy zdjęciu ze stosu (i pomijanie już odwiedzonych) gwarantuje, że każdy węzeł zostanie przetworzony raz, a czas działania pozostanie O(V + E). Częsty błąd to oznaczanie węzłów tylko przy zdejmowaniu przy jednoczesnym kładzeniu duplikatów: to poprawne, ale zostawia na stosie nieaktualne wpisy, które trzeba pomijać.
Ilustracja języków programowania w Coddy

Opanuj algorytmy z Coddy

ZACZNIJ