Menu
Coddy logo textTech

DFS (visita in profondità)

Ultimo aggiornamento

La visita in profondità esplora un grafo scendendo il più in profondità possibile lungo ogni ramo prima di tornare indietro. Partendo da un nodo sorgente, visita un vicino, poi il vicino di quel vicino e così via, finché non arriva a un vicolo cieco (un nodo senza vicini non visitati); a quel punto torna indietro e prova il ramo successivo. Premi play qui sopra per vederla scendere lungo un percorso, toccare il fondo e risalire.

La DFS si esprime in modo naturale con una pila: una pila esplicita (come nell'animazione) oppure lo stack delle chiamate tramite la ricorsione. Visita ogni nodo e ogni arco una volta, quindi richiede tempo O(V + E) per un grafo con V vertici ed E archi.

Complessità temporale e spaziale

MisuraComplessitàNote
TempoO(V + E)Ogni vertice e ogni arco visitati una volta
SpazioO(V)Pila + insieme dei visitati, nel caso peggiore tutti i nodi
VisitaPrima in profonditàSegue un ramo fino in fondo prima degli altri
Struttura datiPila (o ricorsione)L'ordine LIFO determina il comportamento in profondità

Passo dopo passo

PassoCosa succede
1Inserisci il nodo sorgente nella pila.
2Estrai un nodo; se è già stato visitato, saltalo.
3Segnalo come visitato e registra l'arco che l'ha raggiunto.
4Inserisci nella pila tutti i suoi vicini non visitati.
5Ripeti finché la pila non è vuota.

Esempio svolto

DFS iterativa da A sul grafo A → [B, C], B → [D], C → [E], inserendo i vicini nell'ordine elencato (la pila LIFO estrae per primo l'ultimo inserito):

PassoPila (cima a destra)VisitatiAzione
1[A]{}Inserisci la sorgente A.
2[B, C]{A}Estrai A, segnalo come visitato, inserisci i vicini B e poi C.
3[B, E]{A, C}Estrai C, segnalo come visitato, inserisci il vicino E.
4[B]{A, C, E}Estrai E, segnalo come visitato, nessun vicino.
5[D]{A, C, E, B}Estrai B, segnalo come visitato, inserisci il vicino D.
6[]{A, C, E, B, D}Estrai D, segnalo come visitato, pila vuota: fatto.

Quando usare la DFS

Usala quandoEvitala quando
Ti serve rilevare cicli, fare un ordinamento topologico o trovare le componenti connesse.Ti serve il cammino minimo in un grafo non pesato: lo fa la BFS, non la DFS.
Il grafo è largo e poco profondo, quindi una pila usa molta meno memoria di una coda con tanti nodi di frontiera.Il grafo è molto profondo e usi la ricorsione: lo stack delle chiamate può andare in overflow.
Vuoi esplorare tutti i percorsi in modo esaustivo, come nella risoluzione di labirinti o nella ricerca con backtracking.Ti servono i nodi scoperti in ordine di distanza dalla sorgente.
Stai verificando la raggiungibilità o se esiste un percorso tra due nodi.Devi garantire il numero minimo di archi nel percorso trovato.

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

Codice Depth-First Search in Python

Python
1def dfs(graph, node, visited):2    visited.add(node)3    order = [node]4    for neighbor in graph[node]:5        if neighbor not in visited:6            order.extend(dfs(graph, neighbor, visited))7    return order8
9
10graph = {11    "A": ["B", "C"],12    "B": ["D", "E"],13    "C": ["F"],14    "D": [],15    "E": ["F"],16    "F": [],17}18
19order = dfs(graph, "A", set())20print("DFS order:", " -> ".join(order))
Esegui questo codice nel playground Python

Domande frequenti sulla DFS

Qual è la complessità temporale della DFS?
La DFS richiede tempo O(V + E), dove V è il numero di vertici ed E il numero di archi, perché visita ogni vertice una volta ed esamina ogni arco una volta. Usa spazio O(V) per la pila e l'insieme dei visitati.
Qual è la differenza tra DFS e BFS?
La DFS usa una pila e scende in profondità lungo un ramo prima di tornare indietro, mentre la BFS usa una coda ed esplora livello per livello, prima i nodi più vicini. La BFS trova il cammino minimo in un grafo non pesato; la DFS no, ma usa meno memoria sui grafi larghi ed è naturale per compiti come il rilevamento di cicli e l'ordinamento topologico.
La DFS è ricorsiva o iterativa?
Funzionano entrambe. La versione ricorsiva usa implicitamente lo stack delle chiamate del programma ed è molto concisa; la versione iterativa usa una pila esplicita (come l'animazione qui) ed evita gli stack overflow da ricorsione profonda sui grafi grandi.
Quando conviene usare la DFS invece della BFS?
Usa la DFS quando devi esplorare percorsi interi: rilevamento di cicli, ordinamento topologico, componenti connesse o ricerca con backtracking come la risoluzione di un labirinto. Usa anche meno memoria sui grafi larghi, perché la pila contiene solo la frontiera di un ramo alla volta. Scegli invece la BFS quando ti serve il cammino minimo in un grafo non pesato o i nodi in ordine di distanza dalla sorgente.
La DFS può trovare il cammino minimo?
Non in modo affidabile. La DFS restituisce il primo percorso che trova, che spesso non è quello con meno archi, perché punta a scendere in profondità invece di espandersi in modo uniforme. Per i cammini minimi in un grafo non pesato usa la BFS, e per i grafi pesati usa l'algoritmo di Dijkstra.
Perché la DFS ha bisogno di un insieme dei visitati?
Senza un insieme dei visitati, la DFS rivisita i nodi raggiungibili da più percorsi e, in un grafo con cicli, gira all'infinito. Segnare un nodo come visitato quando lo estrai (e saltare le estrazioni di nodi già visitati) garantisce che ogni nodo venga elaborato una volta e mantiene il tempo a O(V + E). Un errore comune è segnare i nodi come visitati solo all'estrazione continuando a inserire duplicati: è corretto, ma può lasciare nella pila voci vecchie da saltare.
Illustrazione dei linguaggi di programmazione di Coddy

Padroneggia gli algoritmi con Coddy

INIZIA