0Pricing
Coding Interview Prep · Lezione

Ordinamento topologico DFS post-order

Esegua la DFS e inserisca ogni nodo in uno stack dopo aver esplorato completamente i suoi vicini, quindi estragga gli elementi dallo stack per ottenere un ordine topologico valido.

Ordinamento topologico DFS post-order è una lezione Coding Interview Prep gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Coding Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Coding Interview Prep include 4 lezioni in totale.

Idea dell'ordinamento topologico basato su DFS

Il secondo algoritmo classico per l'ordinamento topologico utilizza DFS con elaborazione post-order. Dopo aver esplorato completamente tutti i vicini di un nodo e i rispettivi discendenti, inserisca il nodo in uno stack. Quando tutti i nodi sono stati elaborati, estragga gli elementi dallo stack per leggere l'ordine topologico. Un nodo inserito nello stack dopo che tutte le sue dipendenze sono state elaborate deve comparire per primo nell'ordine; perciò il post-order inverso è l'ordinamento topologico.

Intuizione alla base del post-order

Consideri un grafo delle dipendenze in cui il corso A richiede il corso B. Quando la DFS visita A, richiama innanzitutto la DFS su B. B non ha prerequisiti, quindi termina per primo e viene inserito per primo nello stack. Poi termina A, che viene a sua volta inserito nello stack. L'estrazione dallo stack restituisce A prima di B, ma alla fine invertiamo l'ordine e otteniamo B prima di A: prima B, poi A. Il post-order inserisce le dipendenze prima degli elementi che dipendono da esse, quindi lo stack invertito è un ordinamento topologico valido.

DFS a tre colori per il rilevamento dei cicli

Utilizzi tre stati per i nodi visitati: WHITE (0) = non visitato, GREY (1) = attualmente in elaborazione (nello stack delle chiamate DFS), BLACK (2) = elaborato completamente. Un arco all'indietro, cioè un arco verso un nodo GREY, indica un ciclo. Gli archi verso nodi BLACK sono sicuri, perché quei nodi sono già stati esplorati completamente. Questo schema a tre colori rileva correttamente tutti i cicli nei grafi orientati.

WHITE, GREY, BLACK = 0, 1, 2
color = [WHITE] * n  # n = number of nodes

# During DFS:
# color[node] = GREY   (entering node)
# recurse into neighbours
# if neighbour is GREY: cycle found!
# color[node] = BLACK  (leaving node, push to stack)

Implementazione completa dell'ordinamento topologico con DFS

Utilizzi una DFS ricorsiva che assegna un colore ai nodi, li inserisce in uno stack in post-order e restituisce False quando rileva un ciclo. Dopo aver visitato tutti i nodi, lo stack, invertito, fornisce l'ordine topologico.

from collections import defaultdict

def dfs_topological_sort(n, edges):
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)
    
    WHITE, GREY, BLACK = 0, 1, 2
    color = [WHITE] * n
    stack = []
    
    def dfs(node):
        color[node] = GREY
        for nxt in graph[node]:
            if color[nxt] == GREY:
                return False  # cycle
            if color[nxt] == WHITE:
                if not dfs(nxt):
                    return False
        color[node] = BLACK
        stack.append(node)
        return True
    
    for i in range(n):
        if color[i] == WHITE:
            if not dfs(i):
                return []  # cycle
    
    return stack[::-1]

print(dfs_topological_sort(4, [(0,1),(0,2),(1,3),(2,3)]))

DFS iterativa per evitare l'overflow dello stack

Il limite di ricorsione di Python, 1000 per impostazione predefinita, può essere problematico per i grafi di grandi dimensioni. Una DFS iterativa che utilizza uno stack esplicito evita questo problema. Il punto chiave è inserire inizialmente (node, False); quando l'elemento viene estratto con valore False, inserisca (node, True), che significa "tornerò qui dopo l'esplorazione", quindi inserisca tutti i vicini non visitati con valore False. Quando l'elemento viene estratto con valore True, assegni al nodo il colore BLACK e lo inserisca nello stack del risultato.

from collections import defaultdict

def dfs_topo_iterative(n, edges):
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)
    
    WHITE, GREY, BLACK = 0, 1, 2
    color = [WHITE] * n
    result = []
    
    for start in range(n):
        if color[start] != WHITE:
            continue
        stack = [(start, False)]
        while stack:
            node, returning = stack.pop()
            if returning:
                color[node] = BLACK
                result.append(node)
            elif color[node] == WHITE:
                color[node] = GREY
                stack.append((node, True))  # will return here
                for nxt in graph[node]:
                    if color[nxt] == WHITE:
                        stack.append((nxt, False))
    
    return result[::-1]

Confronto tra DFS e Kahn's

Entrambi gli algoritmi hanno complessità O(V + E). Differenze principali: Kahn's (BFS) produce naturalmente i nodi nell'ordine delle dipendenze più immediate e offre un rilevamento dei cicli più semplice, tramite il controllo della lunghezza. La DFS post-order funziona in modo ricorsivo e rileva esplicitamente gli archi all'indietro. Kahn's è preferibile quando si desidera ottenere il risultato nell'ordine diretto, senza doverlo invertire. La DFS è preferibile quando serve il post-order completo per altri scopi, come il rilevamento delle SCC. Entrambe le soluzioni sono accettabili nei colloqui tecnici.

Post-order su un albero e su un DAG

In un albero, il post-order visita il sottoalbero sinistro, poi il sottoalbero destro e infine la radice. In un DAG, la DFS in post-order visita tutte le dipendenze di un nodo prima di elaborare il nodo stesso: è la stessa idea generalizzata a più predecessori e a una struttura di grafo arbitraria. La radice di un albero DFS, cioè il nodo di partenza, viene inserita dopo tutti i suoi discendenti, quindi appare per prima nello stack invertito: è la posizione topologica corretta per un nodo senza predecessori.

Alien Dictionary (LeetCode 269)

Alien Dictionary: data una lista ordinata di parole in una lingua aliena, ricavi l'ordinamento dei caratteri. Confronti le parole adiacenti carattere per carattere per trovare la prima differenza: questa determina un arco c1 → c2, che significa che c1 viene prima di c2. Raccolga tutti questi archi ed esegua un ordinamento topologico per ottenere l'ordinamento dei caratteri della lingua aliena. Se esiste un ciclo, l'ordinamento non è valido.

from collections import defaultdict

def alienOrder(words):
    graph = defaultdict(set)
    all_chars = set(c for w in words for c in w)
    
    for i in range(len(words)-1):
        w1, w2 = words[i], words[i+1]
        if len(w1) > len(w2) and w1.startswith(w2):
            return ''  # invalid (prefix comes after)
        for c1, c2 in zip(w1, w2):
            if c1 != c2:
                graph[c1].add(c2)
                break
    
    # DFS topological sort on character graph
    WHITE, GREY, BLACK = 0, 1, 2
    color = {c: WHITE for c in all_chars}
    result = []
    
    def dfs(c):
        color[c] = GREY
        for nxt in graph[c]:
            if color[nxt] == GREY: return False
            if color[nxt] == WHITE and not dfs(nxt): return False
        color[c] = BLACK
        result.append(c)
        return True
    
    for c in all_chars:
        if color[c] == WHITE:
            if not dfs(c): return ''
    return ''.join(result[::-1])

print(alienOrder(['wrt','wrf','er','ett','rftt']))  # 'wertf'

Ordinamento topologico con vincoli

Alcuni problemi richiedono un ordinamento topologico che soddisfi vincoli aggiuntivi, ad esempio il mantenimento dell'ordine relativo degli elementi nella lista originale. Combini l'algoritmo di Kahn's con una coda con priorità personalizzata o con un pre-ordinamento: mantenga l'ordine relativo originale utilizzando un ordinamento stabile sugli elementi della coda a ogni passaggio. Queste varianti con vincoli verificano una comprensione più approfondita della flessibilità dell'algoritmo.

Riconoscere i problemi di ordinamento topologico

Nei problemi da colloquio, alcune espressioni indicano un ordinamento topologico: "date le dipendenze", "prerequisiti", "ordinamento delle attività", "ordine di compilazione", "è possibile completare tutte le attività?", "trovare una sequenza valida". Se il problema richiede di ordinare elementi di cui alcuni devono precedere altri, costruisca un grafo orientato e applichi l'ordinamento topologico di Kahn's o quello basato su DFS. Il rilevamento dei cicli è spesso un requisito secondario dello stesso problema.

Confrontare gli output di DFS e Kahn's

DFS e Kahn's possono produrre ordinamenti topologici validi ma diversi per lo stesso grafo. Entrambi sono corretti: un DAG può avere più ordinamenti topologici validi. Per verificare la correttezza, controlli che per ogni arco u → v del grafo, u compaia prima di v nell'ordine prodotto. Nei problemi da colloquio che richiedono un ordine specifico, ad esempio quello lessicograficamente minore, utilizzi Kahn's con un min-heap: il post-order della DFS non produce naturalmente l'ordine lessicograficamente minore.

Verifica rapida

Verifichi la Sua comprensione dei concetti di Data Structures & Algorithms — Coding Interview Prep trattati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che: l'ordinamento topologico con DFS in post-order inserisce i nodi dopo aver esplorato tutte le loro dipendenze, la marcatura a tre colori (WHITE/GREY/BLACK) rileva i cicli tramite gli archi all'indietro verso i nodi GREY e l'inversione dello stack in post-order produce un ordinamento topologico valido. Ora applicheremo direttamente l'ordinamento topologico ai problemi Course Schedule I e II.

Domande Frequenti

La lezione «Ordinamento topologico DFS post-order» è gratuita?

Sì — il testo completo di «Ordinamento topologico DFS post-order» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Coding Interview Prep, passa a CoddyKit PRO. Il corso Coding Interview Prep include 4 lezioni in totale.

Cosa imparerò in «Ordinamento topologico DFS post-order»?

Esegua la DFS e inserisca ogni nodo in uno stack dopo aver esplorato completamente i suoi vicini, quindi estragga gli elementi dallo stack per ottenere un ordine topologico valido. Eserciti Coding Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Coding Interview Prep?

Non è richiesta alcuna esperienza precedente. Coding Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Ordinamento topologico DFS post-order»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Coding Interview Prep?

Sì. Ogni lezione Coding Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Algoritmo di Kahn: ordinamento topologico BFS
  2. Ordinamento topologico DFS post-order
  3. Course Schedule I e II
  4. Componenti fortemente connesse con Kosaraju
← Torna a Coding Interview Prep