DFS explora una rama del grafo tan profundamente como sea posible antes de retroceder. Utiliza una pila, explícita o implícita mediante recursión.
Recorrer un grafo significa visitar sus vértices siguiendo un criterio sistemático. El recorrido en profundidad, conocido como DFS por Depth-First Search, avanza por una conexión, luego por otra y continúa hasta no encontrar vecinos nuevos.
Cuando alcanza un punto sin salida, retrocede hasta el vértice anterior y prueba otra rama.
DFS sigue tres acciones básicas:
DFS utiliza una estructura LIFO: el último vértice agregado es el primero en procesarse o retirarse.
En una implementación recursiva, la pila de llamadas del lenguaje conserva automáticamente el camino activo.
El orden de DFS depende del vértice inicial y del orden en que se examinan los vecinos. Distintos órdenes pueden producir árboles DFS diferentes, aunque todos visitan la misma componente.
| Decisión | Efecto |
|---|---|
| Cambiar el origen | Modifica el inicio y las ramas |
| Cambiar el orden de vecinos | Modifica el orden de descubrimiento |
| Mantener ambos | Produce un recorrido determinista |
Selecciona un nodo como origen y pulsa Iniciar. Avanza paso a paso para observar la pila, el orden de visita, las aristas de descubrimiento y los retrocesos.
Pila activa
Orden de visita
Acción actual
Verde indica el camino activo de la pila; celeste, vértices ya visitados; rosa, el vértice procesado en el paso actual.
Cuando el vértice actual no tiene vecinos sin visitar, DFS lo retira de la pila y regresa al anterior.
Este mecanismo permite explorar todas las ramas sin perder el camino utilizado para llegar a ellas.
Cada vez que DFS descubre un vértice nuevo, la arista utilizada se incorpora al árbol DFS. Si la componente tiene n vértices, el árbol contiene n − 1 aristas.
Una ejecución desde un origen visita únicamente su componente. Para recorrer todo un grafo desconectado debemos iniciar nuevas búsquedas desde los vértices todavía no visitados.
El resultado es un bosque DFS: un árbol de búsqueda por cada componente conexa.
function dfs(grafo, actual, visitados = new Set()) {
visitados.add(actual);
console.log(actual);
for (const vecino of grafo[actual]) {
if (!visitados.has(vecino)) {
dfs(grafo, vecino, visitados);
}
}
return visitados;
}
const grafo = { A: ["B", "C"], B: ["A", "D"], C: ["A"], D: ["B"] };
dfs(grafo, "A");function dfsIterativo(grafo, origen) {
const visitados = new Set();
const pila = [origen];
while (pila.length) {
const actual = pila.pop();
if (visitados.has(actual)) continue;
visitados.add(actual);
console.log(actual);
for (const vecino of [...grafo[actual]].reverse()) {
if (!visitados.has(vecino)) pila.push(vecino);
}
}
}Con listas de adyacencia, DFS utiliza tiempo O(V + E) y espacio O(V).
| Aplicación | Uso de DFS |
|---|---|
| Conectividad | Marcar todos los vértices alcanzables |
| Detección de ciclos | Reconocer retornos a vértices activos |
| Ordenamiento topológico | Ordenar por tiempos de finalización |
| Laberintos | Explorar caminos mediante retroceso |
DFS explora grafos mediante profundidad y retroceso. Su estructura simple sirve como base para conectividad, ciclos, componentes y numerosos algoritmos avanzados.
En el próximo tema estudiaremos BFS, que explora por niveles utilizando una cola.