Grafi diretti e ordinamento topologico
In questa pagina 5
Nota sulle fonti: ordinamento topologico e DAG sono nel programma ufficiale ma non nel materiale del corso consultato; la nota è scritta dal programma con conoscenze standard.
Grafi diretti
In un grafo diretto ogni arco è una coppia ordinata : è la coda (origine) e la testa (destinazione). Per ogni vertice si distinguono il grado uscente (archi che partono da ) e il grado entrante (archi che arrivano in ); vale .
Cammini e cicli seguono il verso degli archi: un cammino diretto è con ; un ciclo diretto è un cammino con . Un vertice è raggiungibile da se esiste un cammino diretto da a . La rappresentazione con liste di adiacenza mantiene, per ogni vertice, la lista dei soli archi uscenti (e, se serve, quella degli entranti): lo spazio è e ogni arco compare una volta (vedi Rappresentazione dei grafiStrutture di base (lista dei vertici LV, lista degli archi LE), liste di adiacenza, matrice di adiacenza; operazioni incidentEdges, opposite, areAdjacent; spazio e tempi a confronto; esempio con un grafo di 5 vertici.Rappresentazione dei grafi →).
Visite. BFS e DFS (vedi Visite di grafi - BFS e DFSVisite in ampiezza (BFS) e in profondità (DFS) come design pattern; etichette discovery, cross e back edge; BFS tree e distanze; complessità Theta(n+m) con liste di adiacenza; applicazioni (connettività, componenti, spanning tree, cammini minimi non pesati, cicli, vertici a distanza al più d); esempio svolto su un grafo di 7 vertici.Visite di grafi - BFS e DFS →) funzionano seguendo gli archi uscenti; visitano i vertici raggiungibili dalla sorgente e costano (ogni arco è esaminato una volta). Nella DFS gli archi non-albero si distinguono in: back edge (verso un antenato nell'albero DFS), forward edge (verso un discendente già visitato) e cross edge (verso un vertice in un ramo già concluso).
DAG
Un DAG (directed acyclic graph) è un grafo diretto senza cicli diretti. Modella dipendenze: prerequisiti di corsi, compiti di un progetto, dipendenze tra moduli di un programma.
Proposizione. Un grafo diretto ha un ciclo diretto una DFS incontra un back edge. Idea: un back edge con antenato di chiude, insieme al cammino albero da a , un ciclo; viceversa, se esiste un ciclo, il primo suo vertice visitato dalla DFS ha tutti gli altri come discendenti e l'arco del ciclo che torna a lui è un back edge.
Ordinamento topologico
Un ordinamento topologico di un DAG è una sequenza di tutti i vertici tale che per ogni arco il vertice precede . Esiste se e solo se il grafo è un DAG (con un ciclo, ogni vertice del ciclo dovrebbe precedere il successivo: impossibile). In generale non è unico.
Algoritmo con i gradi entranti
Un vertice con non ha prerequisiti e può essere messo in testa. Si mette in una coda, si "toglie" dal grafo (si decrementa il grado entrante dei suoi successori) e si ripete.
Algoritmo topologicalSort(G)
Input: grafo diretto G Output: ordine topologico, oppure "ciclo"
forall v in V do v.in <- grado entrante di v
Q <- coda con i vertici v con v.in = 0; ordine <- lista vuota
while Q non è vuota do
u <- Q.dequeue(); aggiungi u a ordine
forall (u -> v) in E do
v.in <- v.in - 1
if v.in = 0 then Q.enqueue(v)
if ordine ha n vertici then return ordine else return "ciclo"Invariante: v.in è il numero di predecessori di non ancora in ordine; quindi un vertice entra in coda, e poi in ordine, solo quando tutti i suoi predecessori sono già in ordine prima di lui. Costo: il calcolo dei gradi , ogni vertice entra ed esce dalla coda una volta, ogni arco è considerato una volta: . Se alla fine mancano vertici, restano vertici con predecessori tutti non rimossi: c'è un ciclo.
Algoritmo con la DFS
Si esegue una DFS su tutto il grafo; quando un vertice termina (tutti i suoi successori sono stati esplorati) lo si mette in una lista; l'ordine topologico è la lista rovesciata (reverse postorder).
Correttezza. Per ogni arco in un DAG, termina prima di : se è già finito quando si esamina l'arco, ovvio; se non è ancora visitato viene esplorato (e finito) prima che termini; non può essere in corso (ci sarebbe un back edge e quindi un ciclo). Rovesciando, precede . Costo .
Esempio svolto
DAG con vertici e archi , , , , , . Gradi entranti: , , , , , .
- Con i gradi entranti: coda iniziale . Estrae : passa a (entra), a . Estrae : a (entra). Estrae : a . Estrae : a (entra). Estrae : entra. Estrae . Ordine: .
- Con la DFS (da , poi , successori in ordine alfabetico): fine visita ; rovesciato: .
Entrambi sono ordini topologici validi (si controlla ogni arco), e sono diversi: l'ordinamento non è unico. (Verificati eseguendo le due implementazioni.)
Applicazione: cammini minimi in un DAG pesato in , anche con pesi negativi: si rilassano gli archi uscenti dei vertici nell'ordine topologico a partire dalla sorgente (quando si arriva a tutti i predecessori sono già definitivi).
Errori comuni
- Cercare un ordine topologico in un grafo con cicli (non esiste): l'algoritmo con i gradi entranti lo segnala con
ordineincompleto. - Dimenticare di rovesciare la lista nell'algoritmo con la DFS (si ottiene un ordine inverso).
- Credere che l'ordine sia unico.
- Trattare un grafo diretto come non diretto nelle visite: si seguono solo gli archi uscenti, quindi non si visitano necessariamente tutti i vertici.
Versione ripasso
- Grafo diretto: archi ; grado uscente e entrante, ; cammino/ciclo diretti; liste di adiacenza con i soli archi uscenti, ogni arco una volta (vedi Rappresentazione dei grafiStrutture di base (lista dei vertici LV, lista degli archi LE), liste di adiacenza, matrice di adiacenza; operazioni incidentEdges, opposite, areAdjacent; spazio e tempi a confronto; esempio con un grafo di 5 vertici.Rappresentazione dei grafi →).
- Visite seguono gli archi uscenti, (vedi Visite di grafi - BFS e DFSVisite in ampiezza (BFS) e in profondità (DFS) come design pattern; etichette discovery, cross e back edge; BFS tree e distanze; complessità Theta(n+m) con liste di adiacenza; applicazioni (connettività, componenti, spanning tree, cammini minimi non pesati, cicli, vertici a distanza al più d); esempio svolto su un grafo di 7 vertici.Visite di grafi - BFS e DFS →); archi non-albero della DFS: back, forward, cross.
- DAG = senza cicli diretti; ciclo DFS trova un back edge.
- Ordine topologico: per ogni arco , prima di ; esiste DAG; non unico.
- Con gradi entranti: coda dei vertici con ; estrae , decrementa i successori, accoda quelli a ; se
ordineha vertici c'è un ciclo; . - Con DFS: reverse postorder (vertice in lista quando termina, lista rovesciata); per ogni arco finisce prima di ; .
- Esempio (, , , , , ): gradi entranti ; DFS .
- Pseudocodice (gradi entranti):
v.in <- grado entrante; vertici conin = 0; finché non è vuota:dequeue, aggiungi aordine, per ogni :v.in--, sev.in = 0accoda ;ordinecon vertici ⇒ ciclo. - DFS: vertice in lista quando termina, ordine = lista rovesciata; per ogni arco in un DAG termina prima di .
- Errori: ordine topologico in grafi con cicli; lista DFS non rovesciata; ordine creduto unico; visite come se fosse non diretto.