Skip to content
Forge Learn/Graph Algorithms

Dijkstra's Shortest Path

7 min read

You'll learn to

  • -Explain why BFS stops working once graph edges have different weights
  • -Implement dijkstra(graph, start) using a min-heap to always expand the cheapest known node next
  • -Trace Dijkstra's algorithm over a small weighted graph, including a "stale heap entry" case

BFS from the last chapter guarantees the shortest path in an unweighted graph by exploring in order of hop count. The moment edges carry different weights - a road that takes 10 minutes vs. one that takes 1 minute - hop count stops being the right thing to minimize, and BFS quietly gives wrong answers without any obvious sign that anything broke.

Why BFS Breaks Down With Weights

Consider a weighted, directed graph: A -> B costs 4, A -> C costs 1, C -> B costs 2, B -> D costs 1, and C -> D costs 5. BFS, treating every edge as costing the same "1 hop," would report A to B as distance 1 (direct edge) and never consider that going A -> C -> B costs 1 + 2 = 3, which is cheaper than the direct edge's cost of 4 despite being two hops instead of one. Minimizing hop count and minimizing total weight are simply different problems once weights vary, and BFS only solves the former.

Always Expand the Cheapest Known Node Next

Dijkstra's algorithm fixes this with one change in strategy: instead of a plain queue, use a min-heap keyed by total distance-so-far, and always pop and finalize whichever node currently has the smallest known distance from the start. Every time a node is popped, relax its outgoing edges - if reaching a neighbor through this node would be cheaper than the neighbor's current known distance, update it and push the improved distance onto the heap. Because the heap always surfaces the globally cheapest unfinalized node next, once a node is popped, its distance is guaranteed final - no future edge relaxation can ever find something cheaper, since every other node still in the heap already costs at least as much.

dijkstra(graph, start) - a min-heap always expands the cheapest known node next

A Worked Trace

Using the graph from above (A -> B: 4, A -> C: 1, C -> B: 2, B -> D: 1, C -> D: 5), starting from A: the heap begins with (0, A). Popping A relaxes B to 4 and C to 1, pushing (4, B) and (1, C). Popping (1, C) (the smallest in the heap) relaxes B down to 1 + 2 = 3 (better than 4, so update and push (3, B)) and D to 1 + 5 = 6 (push (6, D)). Popping (3, B) relaxes D to 3 + 1 = 4 (better than 6, so update and push (4, D)). Next the heap yields (4, B) - but distances[B] is already 3, so 4 > 3 means this is a stale entry, skipped without doing any work. Then (4, D) is popped and finalized (D has no outgoing edges to relax). Finally (6, D) surfaces as stale too (4 < 6) and is skipped. Final shortest distances from A: A=0, B=3, C=1, D=4 - correctly finding the A -> C -> B -> D route as cheaper than the more "direct-looking" A -> B -> D, exactly the kind of answer BFS could never have produced.

  • -The min-heap replaces BFS's plain queue - it reorders work by cost-so-far instead of discovery order, which is the only change needed to handle weighted edges correctly.
  • -A node's distance is only truly final once it is popped from the heap - until then, a cheaper path might still be found.
  • -The heap can hold multiple stale entries for the same node; the `if current_dist > distances[node]: continue` check is what safely skips them without corrupting the result.
  • -Dijkstra requires non-negative edge weights - a negative edge could make a "finalized" node's distance improve later, breaking the core guarantee the algorithm relies on.

Dijkstra silently gives wrong answers on graphs with negative edge weights, because the "once popped, distance is final" guarantee depends on every remaining path only being able to get more expensive, never cheaper. For negative weights, Bellman-Ford is the correct (if slower) tool.

Check Yourself1 / 3

Why does plain BFS fail to find shortest paths once graph edges have different weights?

Ready to Build This?

Level 74: Dijkstra's Shortest Path asks you to implement dijkstra(graph, start), returning shortest distances from start using a min-heap (heapq), exactly as built and traced in this chapter.

ScaleDojo Logo
Initializing ScaleDojo