Algoritmo de Dijkstra con una cola de prioridad
Implemente Dijkstra mediante heapq, siga los pasos de relajación en un grafo ponderado y resuelva cheapest-flights-within-k-stops.
Algoritmo de Dijkstra con una cola de prioridad es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.
Ruta más corta en grafos ponderados
El algoritmo de Dijkstra encuentra la ruta más corta desde un único nodo de origen hasta todos los demás nodos de un grafo ponderado con pesos no negativos en las aristas. Funciona procesando los nodos de forma voraz, en orden de su mejor distancia conocida actual: siempre expande el nodo no visitado más cercano. La estructura de datos clave es un min-heap (cola de prioridad), que recupera de forma eficiente el nodo con la distancia más pequeña.
Descripción general de los pasos del algoritmo
Algoritmo de Dijkstra: (1) Inicialice dist[source] = 0 y dist[all others] = inf. (2) Inserte (0, source) en un min-heap. (3) Extraiga el nodo u con la distancia más pequeña. Si ya se ha visitado con una distancia menor, omítalo. (4) Para cada vecino v de u: si dist[u] + weight(u,v) < dist[v], actualice dist[v] e inserte (dist[v], v) en el heap. (5) Repita hasta que el heap esté vacío.
Implementación en Python con heapq
heapq de Python implementa un min-heap. Representamos el grafo como una lista de adyacencia: graph[u] = [(v, weight), ...]. El heap almacena tuplas (distance, node). Utilizamos un conjunto visited para omitir las entradas obsoletas del heap: entradas insertadas antes de encontrar una ruta mejor.
import heapq
def dijkstra(graph, source):
n = len(graph)
dist = [float('inf')] * n
dist[source] = 0
heap = [(0, source)] # (distance, node)
visited = set()
while heap:
d, u = heapq.heappop(heap)
if u in visited:
continue
visited.add(u)
for v, weight in graph[u]:
if dist[u] + weight < dist[v]:
dist[v] = dist[u] + weight
heapq.heappush(heap, (dist[v], v))
return distEjemplo resuelto
Considere un grafo con 5 nodos y las aristas: 0→1 (4), 0→2 (1), 2→1 (2), 1→3 (1), 2→3 (5), 3→4 (3). Las rutas más cortas desde el nodo 0 son: hasta 1 pasando por 0→2→1, con coste 3; hasta 2, con coste 1; hasta 3 pasando por 0→2→1→3, con coste 4; y hasta 4 pasando por 0→2→1→3→4, con coste 7. Dijkstra encuentra todas estas rutas en una sola pasada, no solo la ruta hasta un destino concreto.
import heapq
def dijkstra(graph, source):
dist = [float('inf')] * len(graph)
dist[source] = 0
heap = [(0, source)]
visited = set()
while heap:
d, u = heapq.heappop(heap)
if u in visited:
continue
visited.add(u)
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
heapq.heappush(heap, (dist[v], v))
return dist
graph = [
[(1,4),(2,1)], # 0
[(3,1)], # 1
[(1,2),(3,5)], # 2
[(4,3)], # 3
[] # 4
]
print(dijkstra(graph, 0)) # [0, 3, 1, 4, 7]Por qué Dijkstra falla con pesos negativos
La corrección de Dijkstra se basa en que, una vez que se extrae un nodo del montículo mínimo, su distancia es definitiva. Esto solo se cumple si los pesos de las aristas no son negativos. Con una arista negativa u→v de peso -5, después de visitar v podríamos encontrar un camino más corto que pase por u, pero v ya está marcado como visitado. Una sola arista negativa puede invalidar todos los cálculos de distancias posteriores.
Vuelos más baratos con K escalas (LeetCode 787)
Este problema añade una restricción: como máximo k escalas. Dijkstra estándar no gestiona de forma nativa el recuento de pasos. Solución: ampliar el estado a (cost, node, stops_remaining). Utilice Dijkstra con esta tupla de 3 elementos o Bellman-Ford con k+1 pasadas de relajación. El Dijkstra modificado se detiene cuando stops_remaining llega a 0, lo que impide realizar más saltos.
import heapq
from collections import defaultdict
def findCheapestPrice(n, flights, src, dst, k):
graph = defaultdict(list)
for u, v, w in flights:
graph[u].append((v, w))
heap = [(0, src, k + 1)] # (cost, node, hops_left)
visited = {} # node -> min hops_left seen at this cost level
while heap:
cost, node, hops = heapq.heappop(heap)
if node == dst:
return cost
if hops == 0:
continue
if visited.get(node, 0) >= hops:
continue
visited[node] = hops
for nxt, w in graph[node]:
heapq.heappush(heap, (cost + w, nxt, hops - 1))
return -1
print(findCheapestPrice(4,[[0,1,100],[1,2,100],[0,2,500]],0,2,1)) # 200Análisis de la complejidad temporal
Con un montículo binario, Dijkstra se ejecuta en tiempo O((V + E) log V): cada vértice se extrae una vez (V extracciones), cada arista puede provocar una inserción (E inserciones) y cada operación del montículo cuesta O(log V). Con un montículo de Fibonacci, el límite mejora a O(E + V log V), pero el heapq de Python es un montículo binario. En grafos dispersos (E ≈ V), la versión con montículo binario es O(V log V); en grafos densos (E ≈ V²) es O(V² log V).
Reconstrucción del camino más corto
Para recuperar el camino real (no solo las distancias), mantenga una matriz prev: al actualizar dist[v], establezca prev[v] = u. Cuando el algoritmo termine, reconstruya el camino desde el origen hasta el destino recorriéndolo hacia atrás: comience en dst, siga los punteros de prev hasta source e invierta el resultado.
import heapq
def dijkstra_path(graph, source, target):
n = len(graph)
dist = [float('inf')] * n
prev = [-1] * n
dist[source] = 0
heap = [(0, source)]
visited = set()
while heap:
d, u = heapq.heappop(heap)
if u in visited: continue
visited.add(u)
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
prev[v] = u
heapq.heappush(heap, (dist[v], v))
# Reconstruct
path, node = [], target
while node != -1:
path.append(node)
node = prev[node]
return dist[target], path[::-1]Uso de un diccionario para grafos dispersos
Cuando los nodos son cadenas o enteros no contiguos, utilice un defaultdict(list) para la lista de adyacencia y un dict normal para las distancias. Esto es habitual en problemas de LeetCode como Network Delay Time, donde los nodos están etiquetados del 1 al n. Recuerde utilizar dist = {node: inf for node in all_nodes} y comprobar si hay nodos inalcanzables después del algoritmo.
import heapq
from collections import defaultdict
def networkDelayTime(times, n, k):
graph = defaultdict(list)
for u, v, w in times:
graph[u].append((v, w))
dist = {i: float('inf') for i in range(1, n+1)}
dist[k] = 0
heap = [(0, k)]
while heap:
d, u = heapq.heappop(heap)
if d > dist[u]: continue
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
heapq.heappush(heap, (dist[v], v))
ans = max(dist.values())
return ans if ans < float('inf') else -1
print(networkDelayTime([[2,1,1],[2,3,1],[3,4,1]], 4, 2)) # 2Comparación con BFS para grafos no ponderados
Para grafos no ponderados, BFS encuentra los caminos más cortos en O(V + E), más rápido que Dijkstra, que cuesta O((V+E) log V). Dijkstra generaliza BFS a grafos ponderados mediante una cola de prioridad en lugar de una cola FIFO normal. Cuando todos los pesos de las aristas son iguales, Dijkstra se reduce a BFS. Elija BFS para grafos no ponderados, Dijkstra para pesos no negativos y Bellman-Ford para pesos negativos.
Dijkstra con optimización decrease-key
La implementación de Dijkstra de los libros de texto utiliza una cola de prioridad con decrease-key: cuando mejora la distancia de un nodo, actualiza su prioridad in situ. Esto requiere un montículo de Fibonacci para O(E + V log V), pero es difícil de implementar. El enfoque de eliminación diferida que se utiliza en entrevistas inserta una nueva entrada y omite las extracciones obsoletas; es más sencillo y solo añade una sobrecarga constante. En Python, la eliminación diferida con heapq es la implementación estándar para entrevistas.
Comprobación rápida
Compruebe su comprensión de los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.
Resumen de la lección
En esta lección aprendió que: Dijkstra utiliza un montículo mínimo para procesar de forma voraz los nodos en orden de su mejor distancia actual, se ejecuta en O((V+E) log V) y falla con aristas de peso negativo y las entradas obsoletas del montículo se gestionan comprobando un conjunto de nodos visitados al extraerlas. A continuación veremos Bellman-Ford, que gestiona pesos negativos mediante n-1 pasadas de relajación.
Preguntas frecuentes
¿La lección «Algoritmo de Dijkstra con una cola de prioridad» es gratis?
Sí — el texto completo de «Algoritmo de Dijkstra con una cola de prioridad» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.
¿Qué aprenderé en «Algoritmo de Dijkstra con una cola de prioridad»?
Implemente Dijkstra mediante heapq, siga los pasos de relajación en un grafo ponderado y resuelva cheapest-flights-within-k-stops. Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Coding Interview Prep?
No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Algoritmo de Dijkstra con una cola de prioridad»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?
Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Algoritmo de Dijkstra con una cola de prioridad
- Bellman-Ford y ciclos negativos
- Floyd-Warshall: caminos mínimos entre todos los pares
- Network Delay Time y reconstrucción de caminos