0Pricing
DSA Interview Prep · Lección

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 DSA 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 DSA Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de DSA 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 dist

Ejemplo 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))  # 200

Aná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))  # 2

Comparació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 DSA Interview Prep, actualiza a CoddyKit PRO. El curso de DSA 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 DSA 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 DSA Interview Prep?

No se requiere experiencia previa. DSA 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 DSA Interview Prep?

Sí. Cada lección de DSA 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

  1. Algoritmo de Dijkstra con una cola de prioridad
  2. Bellman-Ford y ciclos negativos
  3. Floyd-Warshall: caminos mínimos entre todos los pares
  4. Network Delay Time y reconstrucción de caminos
← Volver a DSA Interview Prep