La ricerca in profondità, o DFS, è un algoritmo per esplorare un albero o un grafo seguendo un percorso il più lontano possibile prima di fare backtracking. In parole semplici: DFS continua ad andare più in profondità finché non si blocca, poi torna all’ultimo punto in cui è disponibile un altro ramo.

Se devi ricordare una sola idea, ricorda questa: DFS significa "vai in profondità, poi fai backtracking".

Cosa fa la ricerca in profondità

DFS può essere usato sugli alberi e sui grafi generali. Partendo da un nodo, si sposta ripetutamente verso un vicino non visitato. Quando raggiunge un nodo che non ha più vicini non visitati, torna al nodo precedente e continua da lì.

Questo backtracking è il comportamento che la definisce. Nelle implementazioni ricorsive, lo stack delle chiamate lo gestisce automaticamente. Nelle implementazioni iterative, uno stack esplicito svolge lo stesso ruolo.

Perché l’ordine di DFS può cambiare

DFS non produce un unico ordine di visita universale. L’ordine esatto dipende dall’ordine in cui vengono considerati i vicini.

Per esempio, se un nodo ha come vicini BB e CC, visitare prima BB può dare un ordine DFS diverso rispetto a visitare prima CC. In entrambi i casi la ricerca è comunque DFS, perché la regola è la stessa: continuare ad andare in profondità prima di esplorare un ramo fratello.

Esempio di DFS su un piccolo grafo

Supponiamo che il grafo abbia queste connessioni:

  • AA è collegato a BB e CC
  • BB è collegato a DD e EE
  • CC è collegato a FF

Supponiamo di esaminare sempre i vicini da sinistra a destra e di partire da AA.

DFS procede così:

  1. Visita AA
  2. Vai a BB
  3. Vai a DD
  4. DD non ha vicini non visitati, quindi fai backtracking fino a BB
  5. Da BB, vai a EE
  6. EE è completato, quindi fai backtracking fino a BB, poi fino a AA
  7. Da AA, vai a CC
  8. Da CC, vai a FF

Quindi un possibile ordine di visita DFS è:

A,B,D,E,C,FA, B, D, E, C, F

L’idea chiave è che DFS completa il lato ABA \to B prima di iniziare il lato ACA \to C. Se cambiassi l’ordine dei vicini, potrebbe cambiare anche l’ordine di visita.

Perché il backtracking usa uno stack

DFS ha sempre bisogno di un modo per ricordare dove tornare dopo aver raggiunto un vicolo cieco. Per questo gli stack compaiono naturalmente in DFS.

Nella DFS ricorsiva, ogni chiamata di funzione resta in attesa mentre la ricerca va più in profondità, e il programma ritorna in ordine inverso quando fa backtracking. Nella DFS iterativa, inserisci i nodi in uno stack e li estrai quando è il momento di continuare dal punto incompleto più recente.

Complessità temporale e spaziale di DFS

Se il grafo è memorizzato come lista di adiacenza e ogni vertice viene segnato la prima volta che viene visitato, DFS richiede

O(V+E)O(V + E)

tempo per la parte raggiungibile del grafo, dove VV è il numero di vertici ed EE è il numero di archi.

Lo spazio aggiuntivo è tipicamente

O(V)O(V)

perché la struttura dei visitati e lo stack di ricorsione o lo stack esplicito possono entrambi crescere con il numero di vertici.

Errori comuni in DFS

Dimenticare di segnare i nodi visitati

In un grafo generale, dimenticare il controllo dei visitati può far entrare DFS in un ciclo all’infinito. Anche in un albero memorizzato come grafo non orientato, devi comunque evitare di tornare subito al nodo padre, a meno che il tuo codice gestisca esplicitamente quel caso.

Supporre che DFS trovi il cammino più breve

DFS può trovare un cammino, ma in generale non garantisce il cammino più breve in un grafo non pesato. Se l’obiettivo è il cammino più breve e tutti gli archi hanno lo stesso peso, BFS è di solito il punto di partenza giusto.

Considerare l’ordine DFS come unico

L’ordine dipende dall’ordine dei vicini. Se quell’ordine cambia, può cambiare anche l’ordine di attraversamento.

Quando si usa la ricerca in profondità

DFS è utile quando vuoi esplorare la struttura invece di cercare prima solo la distanza minima. Gli usi comuni includono:

  • verificare se un nodo è raggiungibile
  • trovare le componenti connesse
  • rilevare cicli in alcune configurazioni di grafi
  • attraversare alberi
  • risolvere problemi di backtracking come labirinti o ricerca di soluzioni in puzzle

Il motivo principale per cui compare così spesso è che "vai in profondità, poi fai backtracking" si adatta a molte strutture di problemi ricorsivi.

DFS vs BFS: la differenza pratica

DFS scende prima lungo un ramo. BFS esplora tutti i nodi a una certa distanza prima di passare alla distanza successiva.

Se ti interessa un’esplorazione completa o una struttura ricorsiva, DFS è spesso naturale. Se ti interessa il cammino più breve in un grafo non pesato, BFS è spesso la scelta iniziale più sicura.

Prova un attraversamento simile

Disegna un piccolo grafo con sei nodi e scegli un ordine fisso dei vicini. Esegui DFS a mano e annota esattamente quando vai avanti e quando fai backtracking. Se il tuo ordine cambia dopo aver riordinato i vicini, non è un errore. Fa parte del funzionamento di DFS.

Hai bisogno di aiuto con un problema?

Carica la tua domanda e ottieni una soluzione verificata, passo dopo passo, in pochi secondi.

Apri GPAI Solver →