Menu
Coddy logo textTech

Topological sort (ordinamento topologico)

Ultimo aggiornamento

Un ordinamento topologico di un grafo aciclico orientato (DAG) è un ordine lineare dei suoi nodi tale che, per ogni arco u → v, u viene prima di v. Risponde a domande come "in che ordine posso eseguire questi compiti in modo che ogni prerequisito finisca prima?" Premi Play qui sopra per vedere l'algoritmo di Kahn estrarre i nodi in un ordine valido.

L'algoritmo di Kahn prende ripetutamente un nodo senza archi entranti rimasti (grado entrante 0), lo aggiunge all'ordine e rimuove i suoi archi uscenti, cosa che può liberare nuovi nodi con grado entrante 0. Funziona solo su un DAG: se esiste un ciclo, alcuni nodi non arrivano mai a grado entrante 0 e non esiste un ordine valido. Richiede tempo O(V + E).

Complessità temporale e spaziale

MisuraComplessitàNote
TempoO(V + E)Ogni nodo emesso una volta, ogni arco rimosso una volta
SpazioO(V)Gradi entranti + insieme dei pronti + ordine
RichiedeUn DAGI cicli non hanno un ordinamento topologico
RisultatoNon unicoPossono esistere molti ordini validi

Passo dopo passo (algoritmo di Kahn)

PassoCosa succede
1Calcola il grado entrante (numero di archi entranti) di ogni nodo.
2Raccogli tutti i nodi con grado entrante 0 in un insieme dei pronti.
3Prendi un nodo pronto e aggiungilo all'ordine di uscita.
4Decrementa il grado entrante di ciascuno dei suoi successori.
5Ogni successore che arriva a grado entrante 0 entra nell'insieme dei pronti.
6Ripeti finché l'insieme dei pronti è vuoto.

Esempio svolto

Ordinamento del DAG con archi A→C, B→C, C→D, C→E, D→F, E→F (gradi entranti iniziali A:0 B:0 C:2 D:1 E:1 F:2):

PassoInsieme dei prontiOrdineAzione
0{A, B}[]A e B partono con grado entrante 0, quindi sono entrambi pronti.
1{B}[A]Emetti A; il suo arco A→C porta il grado entrante di C da 2 → 1.
2{C}[A, B]Emetti B; il suo arco B→C porta C da 1 → 0, quindi C diventa pronto.
3{D, E}[A, B, C]Emetti C; gli archi C→D e C→E portano D ed E a 0, ed entrambi diventano pronti.
4{E}[A, B, C, D]Emetti D; il suo arco D→F porta il grado entrante di F da 2 → 1.
5{F}[A, B, C, D, E]Emetti E; il suo arco E→F porta F da 1 → 0, quindi F diventa pronto.
6{}[A, B, C, D, E, F]Emetti F; l'insieme dei pronti è vuoto e tutti i 6 nodi sono ordinati: fatto.

Quando usare il topological sort

Usalo quandoEvitalo quando
Ti serve un ordine che rispetti le dipendenze (passi di build, installazione di pacchetti, prerequisiti dei corsi).Il grafo non è orientato: l'ordine topologico è definito solo per i grafi orientati.
Il grafo è un DAG e ti basta un qualsiasi ordine lineare valido.Il grafo può contenere cicli e ti serve comunque un ordine totale (non ne esiste nessuno).
Vuoi rilevare i cicli a basso costo: un ordinamento topologico fallito dimostra che ne esiste uno.Ti serve l'ordine più breve o ottimale secondo un peso; il semplice ordinamento topologico ignora i pesi.
Elaborerai l'ordine una sola volta in O(V + E).Gli archi cambiano di continuo e devi riordinare a ogni aggiornamento, caso in cui una struttura incrementale funziona meglio.

Codice Topological Sort

Un'implementazione di Topological Sort pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.

Codice Topological Sort in 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)))
Esegui questo codice nel playground Python

Domande frequenti sul topological sort

A cosa serve l'ordinamento topologico?
Ordina i compiti in modo che ogni dipendenza venga prima di ciò che ne ha bisogno. Usi reali sono i sistemi di build e i gestori di pacchetti (compilare prima le dipendenze), la pianificazione dei corsi con prerequisiti e l'ordine di valutazione delle formule in un foglio di calcolo.
Qual è la complessità del topological sort?
Sia l'algoritmo di Kahn sia l'approccio basato su DFS richiedono tempo O(V + E), perché ogni nodo viene elaborato una volta e ogni arco esaminato una volta. Usano O(V) di spazio aggiuntivo.
Perché l'ordinamento topologico richiede un DAG?
Un ciclo orientato crea una contraddizione: se a deve venire prima di b e b deve venire prima di a, nessun ordine lineare soddisfa entrambe le condizioni. Quindi un ordinamento topologico esiste se e solo se il grafo è un grafo aciclico orientato. L'algoritmo di Kahn rileva un ciclo quando termina prima di aver emesso tutti i nodi.
Che differenza c'è tra l'algoritmo di Kahn e il topological sort con DFS?
L'algoritmo di Kahn è iterativo e simile a una BFS: rimuove ripetutamente i nodi con grado entrante 0, il che rende del tutto espliciti il rilevamento dei cicli e l'ordine. L'approccio DFS visita i nodi in modo ricorsivo e inserisce ciascuno in testa all'ordine quando la sua ricorsione termina, producendo l'inverso dei tempi di completamento. Entrambi sono O(V + E); Kahn evita la ricorsione profonda e fornisce naturalmente l'insieme dei pronti, mentre la DFS è spesso più breve da scrivere.
Quando usare l'ordinamento topologico invece di un ordinamento normale?
Usa l'ordinamento topologico quando l'ordine è definito dalle dipendenze tra gli elementi e non da una chiave confrontabile. Un normale ordinamento per confronto come il mergesort in O(n log n) ordina per valore; l'ordinamento topologico ordina secondo archi "deve venire prima di" e, a differenza di un ordinamento per confronto, può dare molte risposte valide per lo stesso input.
Il risultato di un ordinamento topologico è unico?
Di solito no. Ogni volta che due o più nodi sono pronti (grado entrante 0) nello stesso momento, puoi emetterli in qualsiasi ordine, quindi la maggior parte dei DAG ammette diversi ordinamenti topologici validi. L'ordine è unico solo quando c'è esattamente un nodo pronto a ogni passo, cosa che accade quando il DAG forma un'unica catena (un cammino hamiltoniano).
Illustrazione dei linguaggi di programmazione di Coddy

Padroneggia gli algoritmi con Coddy

INIZIA