Shortest Paths
Finding the cheapest route through a weighted network is one of the most-used algorithms in computing, and a single operation — relaxation — underlies every method. We build the primitive, prove the triangle inequality and optimal substructure that make it work, then meet Dijkstra's algorithm: the greedy solution for non-negative weights, traced vertex by vertex, with the cut argument that proves each extraction is final.
╌╌╌╌
Every navigation app, every network router, every game pathfinder is solving the same problem: given a weighted graph, find the cheapest route from one place to another. BFS already solved this when every edge counts as one step. Now the edges carry weights (distances, times, costs), and we want to minimize the total weight along a path. This lesson builds the shortest-path toolkit from a single primitive shared by every algorithm in it.
The problem and its primitive
Every algorithm maintains two arrays. For each vertex , an estimate is an upper bound on , always the true distance, shrinking toward it. A predecessor records the previous vertex on the best path found so far, forming a shortest-path tree. We initialize and for every other vertex.
The one operation that updates these estimates is relaxation: testing whether going through improves our route to .
- 1if then
- 2cheaper route to v via u
- 3
Relaxation never produces an estimate below the true distance, and it can only ever lower an estimate. Every shortest-path algorithm below is a different discipline for deciding which edges to relax, and in what order. Two facts make relaxation work: the triangle inequality, and optimal substructure: any subpath of a shortest path is itself a shortest path.1 The latter is what makes greedy and dynamic-programming approaches both viable.
One relaxation step looks like this. Before, 's best-known route costs ; we test the edge of weight against 's settled estimate . Since , the edge is a shortcut: drops to and is rewired to point back through .
We will trace the algorithms on this small weighted digraph. The negative edge of weight is harmless here (there is no negative cycle), but edges like it are what break Dijkstra and force a dynamic program.
Dijkstra's algorithm
When all edge weights are non-negative, we can be greedy. 's algorithm grows a set of vertices whose shortest distances are finalized. At each step it picks the non-finalized vertex with the smallest estimate , finalizes it, and relaxes its outgoing edges. A min-priority queue keyed by supplies the next vertex.
- 1foreach vertex do
- 2
- 3
- 4
- 5
- 6min-PQ keyed by d
- 7while do
- 8closest unfinalized
- 9u.d now final
- 10foreach adjacent to do
- 11callDecrease-Key updates Q
- 12return and
Correctness rests on a cut argument — the same versus split that powered the exchange proofs for minimum spanning trees. The correctness claim is a loop invariant, and unwinding it across the whole run gives the theorem that justifies the greedy commitment.
The non-negativity is doing all the work in that last inequality: it guarantees extending a path never decreases its cost, so the closest frontier vertex can be safely frozen. A single negative edge breaks , so the greedy commitment becomes unsound; the failure is exhibited concretely below.
A complete run
Here is a full run: every , every successful
relaxation, and the queue contents after each step. The graph has vertices
and edges (), (), (),
(), (), (), (). Read a
table entry as with
; bold marks a finalized
estimate, which never moves again.
| Step | Extracted (key) | Queue after (vertex: key) | |||||
|---|---|---|---|---|---|---|---|
| init | — | ||||||
| 1 | |||||||
| 2 | |||||||
| 3 | |||||||
| 4 | |||||||
| 5 | — |
Two steps in the trace show the mechanism. In step 2, extracting triggers a on : the tentative (the direct edge) is beaten by through , so 's queue key drops and its predecessor is rewired. In step 3, the same thing happens to : falls to . Both improvements arrive before the affected vertex is extracted; the theorem guarantees this ordering can never fail with non-negative weights. The final predecessor array spells out the shortest-path tree: .
Vertices finalize in nondecreasing order of distance () — a direct consequence of the greedy invariant. Notice that is finalized at distance via the two-hop route , beating the direct edge of weight — the relaxation through fired before was ever extracted:
Running time. Like Prim, Dijkstra does exactly operations (each vertex leaves the queue once) and at most operations (each edge is relaxed once, when its tail is extracted, and each successful relaxation is one key decrease). The total is therefore
With a binary heap both operations cost , giving , which is whenever every vertex is reachable, since then . A Fibonacci heap makes amortized , improving the bound to . The gap matters most on dense graphs: with , the binary heap pays while the Fibonacci heap pays .
Why negative edges break it
The theorem leaned on non-negativity exactly once, in the step the rest of only adds cost
, and one negative edge is enough to break it. Take three vertices:
with weight , with weight , and with weight
. The true distance to is via . But
Dijkstra extracts , then extracts (key , the current minimum) and
freezes . Only afterward does it extract and try the edge
: the relaxation would succeed, but has already
left the queue, and the algorithm never revisits a finalized vertex. The
greedy schedule processed before the cheap route to it existed.
A tempting repair, adding a constant to every edge weight until none is negative, fails because it penalizes paths in proportion to their hop count: a three-edge path gains while a one-edge path gains only , so the reweighted graph can have a different shortest path. Handling negative edges requires giving up the greedy schedule in favor of dynamic programming.
How a map app really routes
Dijkstra explores in every direction at once, which is wasteful on a continent-sized road network. Production route planners keep the relaxation primitive but prune the search hard.
A* search. Give the algorithm a heuristic , a lower bound on the remaining distance from to the target (straight-line distance on a map). A* extracts the vertex minimizing instead of , biasing the frontier toward the goal.2 When is admissible (never overestimates) and consistent (), A* returns an exact shortest path while touching far fewer vertices than Dijkstra — and with it is Dijkstra, so the two sit on one spectrum. A* is really Dijkstra on the reweighted graph , and it is consistency that keeps those weights non-negative.
Bidirectional search runs two Dijkstras at once, one forward from and one backward from , and stops when their frontiers meet; each explores roughly a hemisphere instead of a full ball, halving the exponent of the searched area.
Contraction hierarchies go further for the road-network case where the graph is fixed and queried millions of times.3 A one-time preprocessing pass ranks vertices by importance and adds shortcut edges that bypass unimportant ones, so a query only ever climbs the hierarchy from and toward their meeting point. After preprocessing, continent-scale point-to-point queries finish in microseconds — the machinery behind the instant routes in a navigation app.
On the theory side, Duan, Mao, Mao, Shu, and Yin (2025) gave the first SSSP algorithm to break Dijkstra's sorting bottleneck on directed graphs with real non-negative weights, running in — evidence that even this settled-seeming problem still has room below .4
This continues in All-Pairs and Negative Weights, where we give up the greedy schedule to handle negative edges (Bellman-Ford as a dynamic program) and compute the distance between every pair of vertices (Floyd-Warshall).
Footnotes
- CLRS, Ch. 24 & 25 — Single-Source and All-Pairs Shortest Paths — relaxation, the triangle inequality, and optimal substructure. ↩
- Hart, Nilsson & Raphael (1968),
A Formal Basis for the Heuristic Determination of Minimum Cost Paths,
IEEE Trans. Systems Science and Cybernetics 4(2), 100–107 — the A* algorithm and its admissibility conditions. ↩ - Geisberger, Sanders, Schultes & Delling (2008),
Contraction Hierarchies: Faster and Simpler Hierarchical Routing in Road Networks,
Proc. WEA 2008 — shortcut-based preprocessing for fast road-network queries. ↩ - Duan, Mao, Mao, Shu & Yin (2025),
Breaking the Sorting Barrier for Directed Single-Source Shortest Paths,
Proc. STOC 2025 — SSSP in , below Dijkstra's sorting bound. ↩
╌╌ END ╌╌