Graphs/Shortest Paths

Lesson 6.41,978 words

Shortest Paths

Finding the cheapest route through a weighted network is one of the most-used algorithms in computing. A single operation — relaxation — underlies them all.

╌╌╌╌

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 .

Algorithm 1:Relax(u,v,w)\textsc{Relax}(u, v, w) — try the edge (u,v)(u,v) as a shortcut to vv
  1. 1
    if u.d+w(u,v)<v.du.d + w(u, v) < v.d then
  2. 2
    v.du.d+w(u,v)v.d \gets u.d + w(u, v)
    cheaper route to v via u
  3. 3
    v.πuv.\pi \gets u

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, believes its best 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 .

One step. The edge beats 's current estimate (), so falls to and its predecessor is rewired to .

We will trace the algorithms on this small weighted digraph. Note the negative edge of weight : it is harmless here (there is no negative cycle), but it is exactly the kind of edge that will break Dijkstra and force us toward a dynamic program.

A small weighted digraph with one negative edge to of weight , used to trace the algorithms.

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.

Algorithm 2:Dijkstra(G,w,s)\textsc{Dijkstra}(G, w, s) — SSSP for non-negative weights
  1. 1
    foreach vertex vVv \in V do
  2. 2
    v.dv.d \gets \infty
  3. 3
    v.πnilv.\pi \gets \text{nil}
  4. 4
    s.d0s.d \gets 0
  5. 5
    SS \gets \emptyset
  6. 6
    QVQ \gets V
    min-PQ keyed by d
  7. 7
    while QQ \neq \emptyset do
  8. 8
    uu \gets Extract-Min(Q)\textsc{Extract-Min}(Q)
    closest unfinalized
  9. 9
    SS{u}S \gets S \cup \set{u}
    u.d now final
  10. 10
    foreach vv adjacent to uu do
  11. 11
    call Relax(u,v,w)\textsc{Relax}(u, v, w)
    Decrease-Key updates Q
  12. 12
    return dd and π\pi

Why the greedy choice is correct. Correctness is framed around a single loop invariant: at the start of each iteration of the while loop, every finalized vertex already holds its true distance, It holds initially ( and ). The maintenance step is the crux of the argument: suppose finalizing were the first move to break the invariant. Then either is set too low or too high.

  • Too low () is impossible, because relaxation never drops an estimate below the true distance — every value of is the cost of some real walk.
  • Too high (): let be a genuine shortest path from to . Walking it from (which is in ), let be the first edge that leaves — so , . By the invariant , and was relaxed when was finalized, so . Now sits on a shortest path to , and because every weight is , the rest of only adds cost: . Hence , so would have returned , not — contradiction.

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.

On a small non-negative digraph the finalization order is exactly nondecreasing in distance, which is the visible signature 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:

Dijkstra finalizes vertices in nondecreasing distance: . Vertex settles at via , beating the direct edge of weight .

Running time. Like Prim, Dijkstra does and up to operations. A binary heap gives ; a Fibonacci heap improves this to .

Bellman-Ford as a dynamic program

Dijkstra breaks when edges can be negative — its greedy finalization assumes a finalized estimate can never improve, which negative edges violate. trades speed for generality. It is best derived not as relax everything repeatedly but as a genuine dynamic program. That framing is worth keeping, because it explains where the comes from.

The subproblem. Bound the number of edges a walk may use. For each vertex and each budget , define

The full answer we want is for , since (as we prove below) shortest paths never need more than edges.

The recurrence. A cheapest walk to using at most edges either uses at most edges already, or it takes one final edge after a cheapest -edge walk to . Minimizing over both:

with base cases and for . This is exactly relaxation: filling row of the table from row is one full pass that relaxes every edge. The tabular form makes the layering explicit, with row holding the best walk of length :

Algorithm 3:Bellman-Ford-DP(G,w,s)\textsc{Bellman-Ford-DP}(G, w, s) — the explicit DP table
  1. 1
    OPT[0,s]0\text{OPT}[0, s] \gets 0; OPT[0,v]\text{OPT}[0, v] \gets \infty for vsv \neq s
  2. 2
    for k1k \gets 1 to n1n - 1 do
    one pass over all edges per row
  3. 3
    foreach vertex vVv \in V do
  4. 4
    OPT[k,v]OPT[k1,v]\text{OPT}[k, v] \gets \text{OPT}[k-1, v]
    inherit: no extra edge
  5. 5
    foreach edge (u,v)E(u, v) \in E do
  6. 6
    if OPT[k1,u]+w(u,v)<OPT[k,v]\text{OPT}[k-1, u] + w(u, v) < \text{OPT}[k, v] then
  7. 7
    OPT[k,v]OPT[k1,u]+w(u,v)\text{OPT}[k, v] \gets \text{OPT}[k-1, u] + w(u, v)
  8. 8
    v.πuv.\pi \gets u
  9. 9
    return OPT[n1,]\text{OPT}[n-1, \cdot] and π\pi

Why stop after rounds? This is the theorem that licenses the whole algorithm. If has no negative-cost cycle, then for all the distance is achieved by a path of length . The proof is a short-circuiting argument: any walk of length visits some vertex twice, so it contains a cycle. Excising that cycle yields a shorter walk of cost no greater than the original (the removed cycle has non-negative cost). Repeating until no cycle remains leaves a simple path, with at most edges, that is no more expensive. So the rows of the DP stop changing by row .

Saving space. The recurrence only ever reads row , so we collapse the table to a single 1-D array updated in place, recovering the familiar form of Bellman-Ford as rounds of relaxing every edge. Note negative edges are fine; only a negative cycle breaks the theorem.

Algorithm 4:Bellman-Ford(G,w,s)\textsc{Bellman-Ford}(G, w, s) — space-saved; with cycle check
  1. 1
    foreach vertex vVv \in V do
  2. 2
    v.dv.d \gets \infty
  3. 3
    v.πnilv.\pi \gets \text{nil}
  4. 4
    s.d0s.d \gets 0
  5. 5
    for i1i \gets 1 to n1n - 1 do
    V-1 full-relaxation rounds
  6. 6
    foreach edge (u,v)E(u, v) \in E do
  7. 7
    call Relax(u,v,w)\textsc{Relax}(u, v, w)
  8. 8
    foreach edge (u,v)E(u, v) \in E do
    extra pass: detect neg cycle
  9. 9
    if u.d+w(u,v)<v.du.d + w(u, v) < v.d then
  10. 10
    return false
    neg cycle reachable from s
  11. 11
    return true

Detecting negative cycles. After rounds, if any edge can still be relaxed, some walk was improved using edges — only possible if a negative cycle lets the cost descend without bound. So one extra pass is exactly the test: relax everything once more, and any successful relaxation certifies a negative cycle reachable from 2 (where cheapest cost to reach a vertex is no longer even well-defined). Dijkstra entirely lacks that capability. The cost is : passes, each relaxing all edges.

Laid out as the DP table , the layering is plain: row holds the cheapest walk of at most edges, each row computed from the one above by a single full pass of relaxation. The entries that improved on each row are shaded; the table stops changing after row , well within the bound:

The Bellman-Ford DP table for the trace digraph. Row = cheapest walk of edges; shaded cells improved that round. Rows freeze after .

Floyd-Warshall: all pairs at once

Sometimes we want for every pair of vertices, a full distance matrix. Running Bellman-Ford from each source costs , but does better with a slick dynamic program over intermediate vertices.

Number the vertices . Let be the weight of the shortest path from to whose intermediate vertices all lie in . Either the shortest such path avoids vertex (then it is ) or it routes through , splitting into an piece and a piece, each using only earlier intermediates:

One round of the recurrence makes the mechanism concrete. Starting from — direct edges only — and admitting intermediates from produces : the entry drops from to via , and the once-unreachable become finite by routing through vertex then . The five matrices below trace one round per permitted intermediate; the entries that improved in each round are shaded:

Floyd-Warshall, one matrix per round. uses only vertices as intermediates; each round's pivot vertex is marked in blue on the indices (its row and column drive that round's updates), and the entries that improved when was admitted are shaded. holds the all-pairs shortest distances.

Three nested loops over , , evaluate this recurrence directly: the outer loop admits one intermediate vertex per round (exactly the five matrices above), and the inner two relax every pair against a route through it.

Algorithm:Floyd-Warshall(W)\textsc{Floyd-Warshall}(W) — all-pairs shortest paths in O(V3)O(V^3)
  1. 1
    dWd \gets W
    dijd_{ij}: edge weight; 00 on the diagonal, else \infty
  2. 2
    for k1k \gets 1 to nn do
    admit vertex kk as an intermediate
  3. 3
    for i1i \gets 1 to nn do
  4. 4
    for j1j \gets 1 to nn do
  5. 5
    if dik+dkj<dijd_{ik} + d_{kj} < d_{ij} then
    routing through kk is shorter
  6. 6
    dijdik+dkjd_{ij} \gets d_{ik} + d_{kj}
  7. 7
    return dd

This is time and space: compact, cache-friendly, and a clean win on dense graphs.3 It handles negative edges, and a negative entry on the diagonal () flags a negative cycle.

Choosing an algorithm

AlgorithmSolvesNegative edges?Time
BFSSSSP, unweightedn/a
DAG relaxationSSSP on a DAGyes
SSSPno
SSSP, + cycle detectionyes
all-pairsyes

The decision tree is simple: unweighted, use BFS; a DAG, relax in topological order; non-negative weights, use Dijkstra (the fastest for one source); negative edges possible, use Bellman-Ford and let it police negative cycles; every pair at once, use Floyd-Warshall. All five are, at heart, disciplined applications of the same primitive.

Takeaways

  • Shortest paths rest on relaxation plus two structural facts: the triangle inequality and optimal substructure.
  • greedily finalizes the closest frontier vertex; correct only for non-negative weights, in with a Fibonacci heap.
  • is a dynamic program: = cheapest walk of edges, and one DP row = one pass of relaxing all edges. Shortest paths need edges (short-circuit any cycle), so rounds suffice; one extra round detects negative cycles. Slower at but handles negative edges.
  • On a DAG, relaxing in topological order solves SSSP in .
  • computes all-pairs shortest paths in via a dynamic program over intermediate vertices.

Footnotes

  1. CLRS, Ch. 24 & 25 — Single-Source and All-Pairs Shortest Paths — relaxation, the triangle inequality, and optimal substructure.
  2. Erickson, Ch. 8 & 9 — Shortest Paths — Bellman-Ford's extra relaxation pass detecting a negative cycle.
  3. Skiena, §6 — Weighted Graph Algorithms — Floyd-Warshall's all-pairs dynamic program.
Practice

╌╌ END ╌╌