Búsqueda en profundidad (DFS)
Última actualización
La búsqueda en profundidad explora un grafo yendo lo más profundo posible por cada rama antes de retroceder. Partiendo de un nodo origen, visita un vecino, luego el vecino de ese vecino, y así sucesivamente hasta llegar a un callejón sin salida (un nodo sin vecinos no visitados), momento en el que retrocede y prueba la siguiente rama. Pulsa reproducir arriba para verla descender por un camino, llegar al fondo y volver a salir.
DFS se expresa de forma natural con una pila: ya sea una pila explícita (como se anima aquí) o la pila de llamadas mediante recursión. Visita cada nodo y arista una vez, por lo que se ejecuta en tiempo O(V + E) para un grafo con V vértices y E aristas.
Complejidad temporal y espacial
| Medida | Complejidad | Notas |
|---|---|---|
| Tiempo | O(V + E) | Cada vértice y arista se visita una vez |
| Espacio | O(V) | Pila + conjunto de visitados, en el peor caso todos los nodos |
| Recorrido | Lo más profundo primero | Sigue una rama hasta el final antes que las demás |
| Estructura de datos | Pila (o recursión) | El orden LIFO impulsa el comportamiento en profundidad |
Paso a paso
| Paso | Qué ocurre |
|---|---|
| 1 | Apila el nodo origen en la pila. |
| 2 | Extrae un nodo; si ya está visitado, sáltalo. |
| 3 | Márcalo como visitado y registra la arista que lo alcanzó. |
| 4 | Apila todos sus vecinos no visitados. |
| 5 | Repite hasta que la pila esté vacía. |
Ejemplo resuelto
DFS iterativo desde A en el grafo A → [B, C], B → [D], C → [E], apilando los vecinos en el orden indicado (la pila LIFO extrae primero el último apilado):
| Paso | Pila (cima a la derecha) | Visitados | Acción |
|---|---|---|---|
| 1 | [A] | {} | Apila el origen A. |
| 2 | [B, C] | {A} | Extrae A, márcalo visitado, apila los vecinos B y luego C. |
| 3 | [B, E] | {A, C} | Extrae C, márcalo visitado, apila el vecino E. |
| 4 | [B] | {A, C, E} | Extrae E, márcalo visitado, sin vecinos. |
| 5 | [D] | {A, C, E, B} | Extrae B, márcalo visitado, apila el vecino D. |
| 6 | [] | {A, C, E, B, D} | Extrae D, márcalo visitado, pila vacía: terminado. |
Cuándo usar DFS
| Úsalo cuando | Evítalo cuando |
|---|---|
| Necesitas detección de ciclos, orden topológico o encontrar componentes conexas. | Necesitas el camino más corto en un grafo no ponderado: eso lo hace BFS, no DFS. |
| El grafo es ancho y poco profundo, por lo que una pila usa mucha menos memoria que una cola con muchos nodos de frontera. | El grafo es muy profundo y usas recursión: la pila de llamadas puede desbordarse. |
| Quieres explorar caminos de forma exhaustiva, como al resolver laberintos o en búsqueda con backtracking. | Necesitas descubrir los nodos por orden de distancia desde el origen. |
| Compruebas la alcanzabilidad o si existe un camino entre dos nodos. | Debes garantizar el mínimo número de aristas en el camino encontrado. |
Código de Depth-First Search
Una implementación limpia y ejecutable de Depth-First Search en Python, JavaScript, Java, C++, C. Elige un lenguaje, copia el código o ábrelo ya cargado en el Playground de Coddy.
Código de Depth-First Search en 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))Código de Depth-First Search en JavaScript
1const graph = {2 A: ["B", "C"],3 B: ["D", "E"],4 C: ["F"],5 D: [],6 E: ["F"],7 F: [],8};9
10function dfs(node, visited = new Set(), order = []) {11 if (visited.has(node)) return order;12 visited.add(node);13 order.push(node);14 for (const next of graph[node]) {15 dfs(next, visited, order);16 }17 return order;18}19
20console.log("DFS from A:", dfs("A").join(" -> "));Código de Depth-First Search en Java
1import java.util.List;2
3public class Main {4 static List<List<Integer>> adj = List.of(5 List.of(1, 2), // neighbors of 06 List.of(0, 3, 4), // neighbors of 17 List.of(0, 5), // neighbors of 28 List.of(1),9 List.of(1, 5),10 List.of(2, 4)11 );12 static boolean[] visited = new boolean[6];13
14 static void dfs(int node) {15 visited[node] = true;16 System.out.print(" " + node);17 for (int next : adj.get(node)) {18 if (!visited[next]) dfs(next);19 }20 }21
22 public static void main(String[] args) {23 System.out.print("DFS from 0:");24 dfs(0);25 System.out.println();26 }27}Código de Depth-First Search en C++
1#include <iostream>2#include <vector>3
4void dfs(int node, const std::vector<std::vector<int>>& adj,5 std::vector<bool>& visited) {6 visited[node] = true;7 std::cout << node << " ";8 // Recurse into every unvisited neighbor9 for (int next : adj[node]) {10 if (!visited[next]) dfs(next, adj, visited);11 }12}13
14int main() {15 // 6-node undirected graph as an adjacency list16 std::vector<std::vector<int>> adj = {17 {1, 2}, // 018 {0, 3, 4}, // 119 {0, 5}, // 220 {1}, // 321 {1, 5}, // 422 {2, 4}, // 523 };24 std::vector<bool> visited(adj.size(), false);25 std::cout << "DFS from node 0: ";26 dfs(0, adj, visited);27 std::cout << "\n";28 return 0;29}Código de Depth-First Search en C
1#include <stdbool.h>2#include <stdio.h>3
4#define N 65
6// 6-node undirected graph as an adjacency matrix7int adj[N][N] = {8 {0, 1, 1, 0, 0, 0},9 {1, 0, 0, 1, 1, 0},10 {1, 0, 0, 0, 0, 1},11 {0, 1, 0, 0, 0, 0},12 {0, 1, 0, 0, 0, 1},13 {0, 0, 1, 0, 1, 0},14};15bool visited[N];16
17void dfs(int node) {18 visited[node] = true;19 printf("%d ", node);20 // Recurse into every unvisited neighbor21 for (int next = 0; next < N; next++) {22 if (adj[node][next] && !visited[next]) dfs(next);23 }24}25
26int main(void) {27 printf("DFS from node 0: ");28 dfs(0);29 printf("\n");30 return 0;31}Preguntas frecuentes sobre la búsqueda en profundidad
¿Cuál es la complejidad temporal de DFS?
O(V + E), donde V es el número de vértices y E el de aristas, porque visita cada vértice una vez y examina cada arista una vez. Usa espacio O(V) para la pila y el conjunto de visitados.¿Cuál es la diferencia entre DFS y BFS?
¿DFS es recursivo o iterativo?
¿Cuándo debería usar DFS en lugar de BFS?
¿Puede DFS encontrar el camino más corto?
¿Por qué DFS necesita un conjunto de visitados?
O(V + E). Un error común es marcar los nodos como visitados solo al extraerlos mientras se siguen apilando duplicados, lo cual es correcto pero puede dejar entradas obsoletas en la pila que hay que saltar.