gl/ripetizioni

Grafi e alberi: visite BFS e DFS per l'esame di algoritmi

Grafi (liste di adiacenza e matrice), visite BFS e DFS con traccia su 6 nodi, costo O(V+E), alberi binari e errori tipici all'esame di Algoritmi.

di Gaetano Livornese

  • #informatica
  • #università
  • #algoritmi
  • #grafi
  • #bfs
  • #dfs

Se segui Algoritmi e strutture dati (informatica o ingegneria), le lezioni sono partite da qualche settimana e di solito è il periodo in cui dopo la complessità arrivano grafi e alberi. L’esame della sessione invernale, tra gennaio e febbraio, cade fra otto e dieci settimane: hai tempo per costruire il metodo con calma. Questo argomento ha un vantaggio: le tracce a mano si imparano a fare, e all’esame sono quasi sempre il modo più sicuro di prendere punti. Prima di questo post conviene avere chiara la complessità con la notazione O-grande; per la rappresentazione in memoria con nodi e riferimenti ti servono anche i puntatori in C.

Rappresentare un grafo

Un grafo ha un insieme di nodi (vertici) e uno di archi . Useremo e anche per i loro numeri, come nei costi. Due rappresentazioni standard:

  • Liste di adiacenza: per ogni nodo, la lista dei suoi vicini. Occupa spazio ed è la scelta naturale per grafi radi.
  • Matrice di adiacenza: una tabella con 1 (o il peso) se esiste l’arco. Occupa e permette di sapere in tempo costante se due nodi sono collegati.

In un grafo non orientato ogni arco compare due volte (in due liste, in due celle simmetriche): la somma dei gradi è .

Il grafo di tutto l’articolo ha 6 nodi e 6 archi: , , , , , . Liste di adiacenza, in ordine alfabetico:

NodoVicini
AB, C
BA, D
CA, D, E
DB, C
EC, F
FE

I gradi sono 2, 2, 3, 2, 2, 1: sommano a 12, cioè con .

BFS: visita in ampiezza con la coda

Si parte da una sorgente, la si marca e la si mette in coda. Finché la coda non è vuota: si estrae il primo nodo e si mettono in coda i suoi vicini non ancora marcati, marcandoli subito.

BFS(G, s):
    marca s; coda = [s]
    finché coda non è vuota:
        u = estrai(coda)
        visita u
        per ogni v in adiacenti(u):
            se v non è marcato:
                marca v; inserisci v in coda

Traccia da :

  1. Estraggo : inserisco . Coda: .
  2. Estraggo : è marcato, inserisco . Coda: .
  3. Estraggo : e già marcati, inserisco . Coda: .
  4. Estraggo : i vicini sono già marcati. Coda: .
  5. Estraggo : inserisco . Coda: .
  6. Estraggo . Coda vuota.

Ordine di visita BFS: . Il livello a cui scopro un nodo è la distanza minima in archi: , , , . Per questo la BFS trova i cammini minimi nei grafi non pesati.

DFS: visita in profondità con ricorsione

Si va avanti sul primo vicino non visitato e si torna indietro solo quando i vicini sono finiti.

DFS(u):
    marca u; visita u
    per ogni v in adiacenti(u):
        se v non è marcato:
            DFS(v)

Traccia da : parto da e vado in ; da il vicino è marcato, quindi vado in ; da , è marcato, vado in ; da , e sono marcati, vado in ; da vado in . Da non c’è altro e torno indietro fino ad .

Ordine di visita DFS: . Rispetto alla BFS cambia: viene prima di , perché la DFS si addentra prima di tornare. La pila delle chiamate ricorsive fa il lavoro che nella BFS fa la coda. Se scrivi la DFS iterativa con una pila esplicita, attenzione all’ordine: se inserisci i vicini in ordine alfabetico, il primo estratto è l’ultimo, quindi l’ordine di visita si inverte rispetto a quello ricorsivo, a meno di inserirli al contrario.

Complessità: O(V+E)

Con le liste di adiacenza, in entrambe le visite ogni nodo viene marcato e processato una volta (costo ) e ogni lista viene scorsa una volta; le liste hanno in tutto elementi per un grafo non orientato, per uno orientato. Il costo è

Con la matrice di adiacenza, per trovare i vicini di un nodo bisogna scorrere un’intera riga di celle: per nodi il costo diventa . Su grafi radi le liste vincono.

Alberi binari: pre-, in- e post-ordine

Un albero è un grafo connesso e senza cicli; l’albero binario ha per ogni nodo al più un figlio sinistro e uno destro. Le visite sono una DFS con tre varianti, secondo il momento in cui visiti la radice:

  • pre-ordine: radice, sottoalbero sinistro, sottoalbero destro;
  • in-ordine: sinistro, radice, destro;
  • post-ordine: sinistro, destro, radice.

Prendiamo l’albero con radice 8, figli di 8: 3 e 10; figli di 3: 1 e 6; figli di 6: 4 e 7; 10 ha solo il figlio destro 14, e 14 ha il solo figlio sinistro 13.

  • Pre-ordine:
  • In-ordine:
  • Post-ordine:
  • Per livelli (la BFS sull’albero):

In questo albero valgono le regole dell’albero binario di ricerca (a sinistra i minori, a destra i maggiori), perciò l’in-ordine esce ordinato. È un’osservazione da esame, ma vale solo per quel tipo di albero.

Gli errori tipici all’esame

Ordine dei vicini. L’ordine di visita dipende dall’ordine con cui sono scritte le liste di adiacenza. Se il testo non lo dice, dichiaralo (“vicini in ordine alfabetico”): con le liste invertite, sul nostro grafo la BFS darebbe e la DFS . Una risposta diversa dalla soluzione può comunque essere corretta, se dichiari la convenzione.

Nodi già visitati. Dimenticare il marcamento significa tornare su nodi già visti e, se c’è un ciclo, non terminare. Un’altra variante: nella BFS marchi il nodo quando lo estrai e non quando lo inserisci, e così lo puoi mettere in coda più volte.

Confondere coda e pila. BFS con la pila non è una BFS.

Pre-, in- e post-ordine. Scrivere la sequenza dalla definizione, partendo dalla radice e scendendo ricorsivamente, non per intuizione.

Costo. Dire per le liste, o senza il : in un grafo non connesso o con nodi isolati il termine non si può tralasciare.

Tre esercizi svolti

1. Cammino minimo con la BFS

Qual è il numero minimo di archi per andare da a nel grafo sopra? Dai un cammino.

Dalla traccia BFS: è scoperto da a livello 3, da a livello 2, da a livello 1. Cammino , 3 archi. Non esiste un cammino più corto: per arrivare a bisogna passare da , unico vicino di , e è raggiungibile solo da , che dista 1 da .

2. Liste o matrice?

Confronta lo spazio per il grafo di 6 nodi e 6 archi, e per un grafo con 1000 nodi e 3000 archi.

Per il nostro grafo la matrice ha celle, le liste elementi (più 6 teste). Con e : la matrice ha celle, le liste circa elementi (più 1000 teste). Il grafo è molto rado, e infatti le liste costano una piccola frazione. Per una visita, il confronto è contro .

3. Alberi: ricostruire dalla coppia di visite

Pre-ordine ; in-ordine . Ricostruisci l’albero e scrivi il post-ordine.

La radice è il primo del pre-ordine: . Nell’in-ordine, stanno a sinistra di e a destra. Il sottoalbero sinistro ha pre-ordine : radice ; nell’in-ordine è a sinistra di e a destra. Quindi ha come figli (con figli e ) e . Post-ordine: .

Metti alla prova

Per grafi e alberi è utile la pagina delle simulazioni per l’università, che raccoglie i quesiti per corso: controlla quanto copre di Algoritmi, perché la copertura dipende dal corso. L’esercizio più utile resta rifare le tracce a mano su un grafo tuo, cambiando sorgente e ordine dei vicini. Anche lo studio di mappe di Karnaugh e algebra di Boole è un argomento di Fondamenti dove la traccia a mano paga allo stesso modo. Altre risorse sono nella pagina informatica; per un confronto 1-a-1 online sulle tracce, puoi partire da una diagnosi gratuita.

Domande frequenti

Qual è la differenza tra visita in ampiezza (BFS) e in profondità (DFS)?

La BFS usa una coda e visita i nodi per livelli di distanza dalla sorgente; la DFS usa una pila (o la ricorsione) e si addentra il più possibile prima di tornare indietro.

Qual è la complessità di BFS e DFS?

Con le liste di adiacenza entrambe costano O(V+E), perché ogni nodo è visitato una volta e ogni lista di adiacenza è scorsa una volta. Con la matrice di adiacenza il costo sale a O(V²).

Perché in una visita bisogna segnare i nodi già visitati?

Se il grafo ha cicli, senza questo controllo si torna all'infinito sugli stessi nodi. Il marcamento garantisce che ogni nodo entri una sola volta in coda o venga esplorato una sola volta.

Quale visita trova il cammino con meno archi tra due nodi?

La BFS: visitando per livelli, il livello a cui un nodo viene scoperto è la sua distanza minima in numero di archi dalla sorgente, in un grafo non pesato.

Continua a leggere

Post correlati.

Vuoi applicare quello che hai letto?

Parliamone 30 minuti gratis: guardiamo insieme dove sei bloccato e ti dico onestamente come posso aiutarti.

Oppure scrivimi su WhatsApp.