Dijkstra's Algorithm
The Intuition
Dijkstra's algorithm computes single-source shortest paths in a weighted graph with non-negative edge weights. It generalizes BFS: instead of a FIFO queue that processes nodes in order of hop count, Dijkstra uses a min-heap that processes nodes in order of total distance from the source.
The greedy choice: at every step, finalize the unfinalized node with the smallest tentative distance. The correctness invariant is that any other path to that node must pass through a different unfinalized node, which by definition has a larger tentative distance, and adding non-negative edges only makes paths longer. So the first time a node is popped from the heap, the recorded distance is the true shortest. This is why negative edges break Dijkstra: an unfinalized node could become reachable via a shorter path through a yet-to-be-explored negative edge, violating the invariant.
The heap can contain stale entries: a node may have been pushed several times with different tentative distances as shorter paths were discovered. The standard handling is "lazy deletion": when popping (d, u), if d > dist[u], the entry is stale, skip it. This avoids the cost of an indexed priority queue while keeping the asymptotics at O((V + E) log V).
Three common variants in interviews:
- Single-target. Stop the loop the moment the target node is popped, since its distance is now final.
- Path reconstruction. Maintain a
prev[v]pointer for the predecessor on the shortest path; trace back from target to source after the run. - 0-1 BFS. When edge weights are only 0 or 1, replace the heap with a deque: push 0-edges to the front, 1-edges to the back. Total
O(V + E). Used for problems like LC 2290 (minimum obstacles to remove).
Shortest path in weighted graphs with non-negative weights, network routing, navigation systems, or any problem asking for minimum cost paths.
- Negative edge weights (use Bellman-Ford)
- Unweighted graph (BFS is simpler and sufficient)
- Need all-pairs shortest path (use Floyd-Warshall)
- Single node graph (distance is zero)
- Target node is unreachable from the source
- Graph contains zero-weight edges
- Graph has negative weights (Dijkstra produces incorrect results)
Key Points
- •Finds shortest path from source to all vertices in weighted graphs
- •Uses a min-heap (priority queue) to always process the nearest unvisited node
- •Does NOT work with negative edge weights. Use Bellman-Ford instead
- •Lazy deletion: skip nodes already finalized when popped from heap
- •Can reconstruct path by tracking predecessors
Code Template
import heapq
def dijkstra(graph, start, n):
"""Shortest paths from start. graph[u] = [(v, weight), ...]"""
dist = [float('inf')] * n
dist[start] = 0
heap = [(0, start)] # (distance, node)
while heap:
d, u = heapq.heappop(heap)
if d > dist[u]:
continue # stale entry
for v, weight in graph[u]:
new_dist = dist[u] + weight
if new_dist < dist[v]:
dist[v] = new_dist
heapq.heappush(heap, (new_dist, v))
return dist
def dijkstra_with_path(graph, start, end, n):
"""Shortest path with reconstruction."""
dist = [float('inf')] * n
dist[start] = 0
prev = [-1] * n
heap = [(0, start)]
while heap:
d, u = heapq.heappop(heap)
if d > dist[u]:
continue
if u == end:
break
for v, weight in graph[u]:
new_dist = dist[u] + weight
if new_dist < dist[v]:
dist[v] = new_dist
prev[v] = u
heapq.heappush(heap, (new_dist, v))
# Reconstruct path
path = []
node = end
while node != -1:
path.append(node)
node = prev[node]
return path[::-1] if dist[end] != float('inf') else []Common Mistakes
- Using with negative edge weights (incorrect results)
- Not using lazy deletion (processing stale entries from the heap)
- Forgetting to initialize distances to infinity