Menu
Coddy logo textTech

Sortowanie topologiczne

Ostatnia aktualizacja

Sortowanie topologiczne skierowanego grafu acyklicznego (DAG) to liniowe uporządkowanie jego wierzchołków takie, że dla każdej krawędzi u → v wierzchołek u występuje przed v. Odpowiada na pytania typu „w jakiej kolejności uruchomić te zadania, żeby każde zadanie wymagane zakończyło się wcześniej?”. Naciśnij Odtwórz powyżej i zobacz, jak algorytm Kahna zdejmuje wierzchołki w poprawnej kolejności.

Algorytm Kahna wielokrotnie bierze wierzchołek bez pozostałych krawędzi wchodzących (stopień wejściowy 0), dopisuje go do wyniku i usuwa jego krawędzie wychodzące, co może zwolnić kolejne wierzchołki o stopniu wejściowym 0. Działa tylko na grafie DAG: jeśli istnieje cykl, niektóre wierzchołki nigdy nie osiągną stopnia wejściowego 0 i poprawna kolejność nie istnieje. Złożoność czasowa to O(V + E).

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

MiaraZłożonośćUwagi
CzasO(V + E)Każdy wierzchołek wypisany raz, każda krawędź usunięta raz
PamięćO(V)Stopnie wejściowe + zbiór gotowych + wynik
WymagaGrafu DAGGraf z cyklami nie ma porządku topologicznego
WynikNie jest jednoznacznyMoże istnieć wiele poprawnych kolejności

Krok po kroku (algorytm Kahna)

KrokCo się dzieje
1Oblicz stopień wejściowy (liczbę krawędzi wchodzących) każdego wierzchołka.
2Zbierz wszystkie wierzchołki o stopniu wejściowym 0 w zbiór gotowych.
3Weź gotowy wierzchołek i dopisz go do wyniku.
4Zmniejsz o jeden stopień wejściowy każdego z jego następników.
5Każdy następnik, którego stopień wejściowy spadnie do 0, trafia do zbioru gotowych.
6Powtarzaj, dopóki zbiór gotowych nie będzie pusty.

Przykład krok po kroku

Sortujemy DAG z krawędziami A→C, B→C, C→D, C→E, D→F, E→F (początkowe stopnie wejściowe A:0 B:0 C:2 D:1 E:1 F:2):

KrokZbiór gotowychKolejnośćDziałanie
0{A, B}[]A i B mają na starcie stopień wejściowy 0, więc oba są gotowe.
1{B}[A]Wypisz A; jego krawędź A→C obniża stopień wejściowy C z 2 → 1.
2{C}[A, B]Wypisz B; jego krawędź B→C obniża C z 1 → 0, więc C staje się gotowy.
3{D, E}[A, B, C]Wypisz C; krawędzie C→D i C→E obniżają D i E do 0, oba stają się gotowe.
4{E}[A, B, C, D]Wypisz D; jego krawędź D→F obniża stopień wejściowy F z 2 → 1.
5{F}[A, B, C, D, E]Wypisz E; jego krawędź E→F obniża F z 1 → 0, więc F staje się gotowy.
6{}[A, B, C, D, E, F]Wypisz F; zbiór gotowych jest pusty, a wszystkie 6 wierzchołków ma już swoje miejsce: koniec.

Kiedy używać sortowania topologicznego

Używaj, gdyUnikaj, gdy
Potrzebujesz kolejności, która respektuje zależności (etapy budowania, instalacja pakietów, wymagania wstępne kursów).Graf jest nieskierowany: porządek topologiczny jest zdefiniowany tylko dla grafów skierowanych.
Graf jest grafem DAG i wystarczy ci dowolna poprawna kolejność liniowa.Graf może zawierać cykle, a mimo to potrzebujesz porządku liniowego (taki nie istnieje).
Chcesz tanio wykrywać cykle: nieudane sortowanie topologiczne dowodzi, że cykl istnieje.Potrzebujesz najkrótszej lub optymalnej kolejności według jakiejś wagi; zwykłe sortowanie topologiczne ignoruje wagi.
Przetwarzasz kolejność jednorazowo w O(V + E).Krawędzie ciągle się zmieniają i trzeba sortować od nowa po każdej zmianie, wtedy lepiej sprawdzi się struktura przyrostowa.

Topological Sort: kod

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

Topological Sort: kod (Python)

Python
1from collections import deque2
3
4def topological_sort(graph):5    # Kahn's algorithm: repeatedly remove nodes with no incoming edges6    in_degree = {node: 0 for node in graph}7    for node in graph:8        for neighbor in graph[node]:9            in_degree[neighbor] += 110    queue = deque(node for node in graph if in_degree[node] == 0)11    order = []12    while queue:13        node = queue.popleft()14        order.append(node)15        for neighbor in graph[node]:16            in_degree[neighbor] -= 117            if in_degree[neighbor] == 0:18                queue.append(neighbor)19    if len(order) != len(graph):20        raise ValueError("Graph has a cycle, no topological order")21    return order22
23
24graph = {25    "shirt": ["tie", "jacket"],26    "tie": ["jacket"],27    "pants": ["shoes", "jacket"],28    "socks": ["shoes"],29    "shoes": [],30    "jacket": [],31}32
33print(" -> ".join(topological_sort(graph)))
Uruchom ten kod w edytorze Python online

Sortowanie topologiczne: najczęstsze pytania

Do czego służy sortowanie topologiczne?
Układa zadania tak, aby każda zależność znalazła się przed tym, co jej potrzebuje. Praktyczne zastosowania to systemy budowania i menedżery pakietów (najpierw kompilacja zależności), planowanie kursów z wymaganiami wstępnymi oraz kolejność obliczania formuł w arkuszach kalkulacyjnych.
Jaka jest złożoność czasowa sortowania topologicznego?
Zarówno algorytm Kahna, jak i podejście oparte na DFS działają w czasie O(V + E), ponieważ każdy wierzchołek jest przetwarzany raz, a każda krawędź sprawdzana raz. Potrzebują O(V) dodatkowej pamięci.
Dlaczego sortowanie topologiczne wymaga grafu DAG?
Skierowany cykl prowadzi do sprzeczności: jeśli a musi być przed b, a b musi być przed a, żadna kolejność liniowa nie spełni obu warunków. Dlatego porządek topologiczny istnieje wtedy i tylko wtedy, gdy graf jest skierowanym grafem acyklicznym. Algorytm Kahna wykrywa cykl, gdy kończy pracę przed wypisaniem wszystkich wierzchołków.
Czym różni się algorytm Kahna od sortowania topologicznego przez DFS?
Algorytm Kahna jest iteracyjny i przypomina BFS: wielokrotnie usuwa wierzchołki o stopniu wejściowym 0, dzięki czemu wykrywanie cykli i ustalanie kolejności są w pełni jawne. Podejście DFS odwiedza wierzchołki rekurencyjnie i dopisuje każdy na początek wyniku, gdy kończy się jego rekurencja, co daje odwrotność czasów zakończenia. Oba mają złożoność O(V + E); algorytm Kahna unika głębokiej rekurencji i naturalnie udostępnia zbiór gotowych, a DFS zwykle wymaga mniej kodu.
Kiedy użyć sortowania topologicznego zamiast zwykłego sortowania?
Sortowania topologicznego używaj wtedy, gdy kolejność wynika z zależności między elementami, a nie z porównywalnego klucza. Zwykłe sortowanie przez porównania, takie jak mergesort w O(n log n), porządkuje według wartości; sortowanie topologiczne porządkuje według krawędzi „musi być przed” i w przeciwieństwie do sortowania przez porównania może dać wiele poprawnych wyników dla tych samych danych.
Czy wynik sortowania topologicznego jest jednoznaczny?
Zwykle nie. Gdy dwa lub więcej wierzchołków jest gotowych (stopień wejściowy 0) jednocześnie, można je wypisać w dowolnej kolejności, więc większość grafów DAG ma kilka poprawnych porządków topologicznych. Kolejność jest jednoznaczna tylko wtedy, gdy na każdym kroku gotowy jest dokładnie jeden wierzchołek, czyli gdy DAG tworzy pojedynczy łańcuch (ścieżkę Hamiltona).
Ilustracja języków programowania w Coddy

Opanuj algorytmy z Coddy

ZACZNIJ