Single-Source Shortest Paths Framework

Imagine a delivery company that must find the cheapest route from a central warehouse to every customer in a city. Each road segment has a different cost due to tolls, fuel, and traffic. Unlike unweighted graphs, where BFS Shortest Paths on Unweighted Graphs finds shortest paths by counting edges, here some edges are expensive and others are nearly free. So the fewest-edges path is not necessarily the cheapest one. This is the single-source shortest paths (SSSP) problem, one of the most widely used abstractions in computer science. It models routing, network latency, currency arbitrage, robot motion planning, and dependency scheduling. The big idea uniting every SSSP algorithm — Bellman-Ford, Dijkstra, DAG relaxation — is a shared framework. Maintain a distance estimate and a predecessor for every vertex. Then repeatedly improve these estimates using a local operation called edge relaxation. Keep doing this until no more improvement is possible. Different algorithms differ only in which edges they relax and in what order. They do not differ in the underlying correctness argument. Understanding this shared skeleton first makes each specific algorithm easier to learn. That's because you already know what problem it solves and what data it produces. This article builds that skeleton. It defines the SSSP problem precisely, and introduces the distance array dd and predecessor array π\pi. Every shortest-path algorithm outputs these two structures. It explains the relaxation operation informally (developed rigorously in Edge Relaxation and Optimal Substructure). It also confronts an unavoidable subtlety: what happens when a graph contains a negative-weight cycle. In that case, "shortest path" may not even be well defined. By the end, you will know exactly what a correct SSSP algorithm must guarantee. You will learn how to detect when no valid answer exists. You will also see how the choice of graph structure (general graph, DAG, non-negative weights) determines which specialized algorithm is right. These specialized algorithms are covered in DAG Shortest Paths and beyond.

Key takeaways
  • SSSP asks for the minimum-weight path from one source vertex to every other vertex in a weighted, directed graph.
  • Every SSSP algorithm maintains a distance estimate array d[v]d[v] and predecessor array π[v]\pi[v], refined via edge relaxation.
  • Relaxation only ever lowers d[v]d[v] toward the truth — it never invalidates a shorter path found earlier.
  • A negative-weight cycle reachable from the source makes some path lengths unbounded below, so shortest paths are undefined for affected vertices.
  • The predecessor array π\pi forms a shortest-paths tree, letting any shortest path be reconstructed by walking back to the source.
  • The right algorithm depends on structure: DAGs use topological order, non-negative weights allow Dijkstra's greedy approach, and general graphs need Bellman-Ford.
The core insight

Every shortest-paths algorithm is the same loop — repeatedly relax edges to shrink distance estimates — differing only in which edges get relaxed and in what order.

The idea

Let G=(V,E)G = (V, E) be a directed graph with a weight function w:E→Rw : E \to \mathbb{R} assigning a real number to every edge (weights may be negative unless stated otherwise). For a path p=⟨v0,v1,…,vk⟩p = \langle v_0, v_1, \dots, v_k \rangle, its weight is

w(p)=∑i=1kw(vi−1,vi).w(p) = \sum_{i=1}^{k} w(v_{i-1}, v_i).

Given a source vertex ss, the shortest-path weight from ss to vv is defined as

δ(s,v)={min⁡{w(p):p is a path from s to v}if some path exists∞if no path exists.\delta(s, v) = \begin{cases} \min\{ w(p) : p \text{ is a path from } s \text{ to } v \} & \text{if some path exists} \\ \infty & \text{if no path exists.} \end{cases}

The single-source shortest paths problem asks for δ(s,v)\delta(s, v) for every vertex v∈Vv \in V, together with enough information to reconstruct an actual shortest path — not merely its length. Every algorithm that solves this problem, no matter how it is organized internally, produces the same two output arrays:

  • Distance array d[v]d[v]: an estimate of δ(s,v)\delta(s, v), initialized to ∞\infty for all v≠sv \ne s and to 00 for ss. A correct algorithm terminates with d[v]=δ(s,v)d[v] = \delta(s,v) for every reachable vv.
  • Predecessor array π[v]\pi[v]: the vertex immediately before vv on the current best-known path from ss, initialized to `NIL`. At termination, following π\pi pointers from any reachable vv back to ss traces out an actual shortest path.

The array π\pi implicitly defines a shortest-paths tree rooted at ss: a tree containing exactly the edges (π[v],v)(\pi[v], v) for every reachable v≠sv \ne s, such that the unique tree path from ss to any vv is a shortest path in GG. This tree is the natural generalization of the BFS tree from unweighted shortest paths — the same idea, but now weighted. The single primitive operation every SSSP algorithm uses to improve its estimates is called relaxing an edge (u,v)(u, v): check whether going through uu offers a cheaper route to vv than the current estimate, and if so, update. This operation is developed carefully — including its optimal-substructure justification — in Edge Relaxation and Optimal Substructure; here we treat it as the shared building block that different algorithms schedule differently.

How it works

The generic relaxation-based framework underlying Bellman-Ford, Dijkstra, and DAG shortest paths is: initialize the distance and predecessor arrays, then relax edges in some order until distances stabilize. The pseudocode below states this framework precisely, using an abstract subroutine `RELAX` that all subsequent algorithms reuse verbatim. ``` INIT-SINGLE-SOURCE(G, s): for each vertex v in G.V: d[v] = infinity pi[v] = NIL d[s] = 0 RELAX(u, v, w): // Try to improve the best known distance to v by routing through u if d[u] + w(u, v) < d[v]: d[v] = d[u] + w(u, v) pi[v] = u return true // an improvement occurred return false GENERIC-SSSP(G, w, s): INIT-SINGLE-SOURCE(G, s) while there exists an edge (u, v) that can still be relaxed: RELAX(u, v, w) return d, pi ``` This "generic" version is not an efficient algorithm by itself — it does not specify which edge to relax next — but every concrete algorithm is an instantiation of it:

  • Bellman-Ford relaxes every edge in the graph, ∣V∣−1|V|-1 times, in arbitrary fixed order, then does one extra pass to detect negative cycles.
  • Dijkstra's algorithm (for graphs with non-negative weights) always relaxes edges out of the vertex with the currently smallest tentative distance, using a priority queue.
  • DAG shortest paths relaxes edges out of vertices in topological order, guaranteeing each vertex is finalized before its edges are used.

The framework guarantees the following loop invariant, which every specific algorithm must preserve: > Upper-bound invariant. At all times during execution, d[v]≥δ(s,v)d[v] \ge \delta(s, v) for every vertex vv, and once d[v]=δ(s,v)d[v] = \delta(s,v) it never changes again if no negative cycle affects vv. Relaxation only ever decreases d[v]d[v], and it only decreases it to a value that corresponds to the weight of an actual s→vs \to v path (namely, the path to uu via π\pi, plus edge (u,v)(u,v)). Since δ(s,v)\delta(s,v) is by definition the minimum over all such path weights, d[v]d[v] can never be forced below δ(s,v)\delta(s,v) by a correct relaxation step. This is why the framework is monotone and safe: it can be applied in almost any order without producing wrong (too-small) answers, though the order determines how quickly it converges to the right (tight) answer.

Detecting the negative-cycle caveat

If the graph contains a cycle CC reachable from ss with total weight w(C)<0w(C) < 0, then any vertex on or reachable from CC has no well-defined shortest path: you can loop around CC arbitrarily many times, each time subtracting more weight, so

δ(s,v)=−∞for all v reachable from a negative cycle reachable from s.\delta(s, v) = -\infty \quad \text{for all } v \text{ reachable from a negative cycle reachable from } s.

A correct SSSP framework must detect this situation rather than silently report a wrong finite number. The standard detection technique piggybacks on the relaxation framework itself: ``` BELLMAN-FORD(G, w, s): INIT-SINGLE-SOURCE(G, s) for i = 1 to |V| - 1: for each edge (u, v) in G.E: RELAX(u, v, w) // Negative-cycle check: after |V|-1 rounds, distances should be stable for each edge (u, v) in G.E: if d[u] + w(u, v) < d[v]: return "NEGATIVE-WEIGHT CYCLE REACHABLE FROM s" return d, pi ``` The check works because of a key fact proved in the correctness section below: if the graph has no negative cycle reachable from ss, then ∣V∣−1|V|-1 full rounds of relaxing every edge are always enough to reach the fixed point d[v]=δ(s,v)d[v] = \delta(s,v) for all reachable vv. So if a further relaxation is still possible after round ∣V∣−1|V|-1, the only explanation is a negative cycle.

Figure
Distance and predecessor arrays forming a shortest-paths tree
s A B C D E 4 2 6 3 1 2 5 shortest-paths tree edge (pi pointer) non-tree edge (relaxed, not improving) d: s=0 A=4 B=2 C=5 D=3 E=6 pi: A←s B←s C←B D←B E←C

Complexity & correctness

Correctness of the framework in general. The upper-bound invariant d[v]≥δ(s,v)d[v] \ge \delta(s,v) holds throughout execution by induction on the number of relaxations performed: initially d[s]=0=δ(s,s)d[s] = 0 = \delta(s,s) and d[v]=∞≥δ(s,v)d[v] = \infty \ge \delta(s,v) for all other vv; each relaxation of edge (u,v)(u,v) sets d[v]←d[u]+w(u,v)d[v] \leftarrow d[u] + w(u,v), and by the inductive hypothesis d[u]≥δ(s,u)d[u] \ge \delta(s,u), so the new d[v]d[v] is at least δ(s,u)+w(u,v)≥δ(s,v)\delta(s,u) + w(u,v) \ge \delta(s,v) (since appending edge (u,v)(u,v) to any path to uu gives some path to vv, and δ(s,v)\delta(s,v) is the minimum over all such paths). This shows d[v]d[v] never drops below the true shortest-path weight.

When does d[v]d[v] reach δ(s,v)\delta(s,v) exactly? This requires that the specific algorithm's relaxation order eventually processes every edge of some shortest path to vv, in order from ss outward. This is precisely the substance of the "path relaxation" lemma proved constructively in Edge Relaxation and Optimal Substructure; the short version is that if ⟨s=v0,v1,…,vk=v⟩\langle s = v_0, v_1, \dots, v_k = v\rangle is a shortest path and edges (v0,v1),(v1,v2),…(v_0,v_1), (v_1,v_2), \dots are relaxed in that relative order (not necessarily consecutively), then after all of them d[v]=δ(s,v)d[v] = \delta(s,v).

Why ∣V∣−1|V|-1 rounds suffice when there is no negative cycle. Any shortest path (if it exists and the graph has no negative cycle reachable from ss) can be taken to be simple — it never repeats a vertex, because removing a cycle from a path only decreases or preserves its weight when all cycles are non-negative, and strictly helps correctness to remove it. A simple path in a graph on ∣V∣|V| vertices has at most ∣V∣−1|V|-1 edges. Bellman-Ford's outer loop relaxes all edges once per round; by induction, after round ii, every vertex whose shortest path uses at most ii edges has d[v]=δ(s,v)d[v] = \delta(s,v). After ∣V∣−1|V|-1 rounds, every simple shortest path — having at most ∣V∣−1|V|-1 edges — has been fully processed, so all reachable d[v]d[v] values are exact.

Why one more round detects a negative cycle. If, after ∣V∣−1|V|-1 rounds, some edge (u,v)(u,v) still admits d[u]+w(u,v)<d[v]d[u] + w(u,v) < d[v], then d[v]d[v] was not the true fixed point — but we just argued ∣V∣−1|V|-1 rounds always suffice in the negative-cycle-free case. So the only way an ∣V∣|V|-th relaxation can still improve something is that the "shortest paths" involved are not simple (they can be shortened indefinitely by looping), which happens exactly when a negative cycle is reachable from ss and can reach vv.

Complexity. Bellman-Ford performs ∣V∣−1|V|-1 rounds, each relaxing all ∣E∣|E| edges, plus one final check pass:

T(∣V∣,∣E∣)=O(∣V∣⋅∣E∣).T(|V|,|E|) = O(|V|\cdot|E|).

Space is O(∣V∣)O(|V|) for the dd and π\pi arrays (plus O(∣V∣+∣E∣)O(|V| + |E|) to store the graph itself). This is the general-purpose cost when weights may be negative and the graph is not a DAG; specialized structure (non-negative weights, or acyclicity) allows strictly faster algorithms, which is exactly the motivation for DAG Shortest Paths (achieving O(∣V∣+∣E∣)O(|V|+|E|) via topological order) and Dijkstra's algorithm (achieving O(∣E∣+∣V∣log⁡∣V∣)O(|E| + |V|\log|V|) with a binary-heap priority queue, when weights are non-negative).

Honesty about tradeoffs. Bellman-Ford's generality is its main appeal — it tolerates negative edges and detects negative cycles — but it pays for this with a quadratic-ish running time in dense graphs. Dijkstra is faster but silently wrong if given negative weights (it may finalize a vertex's distance before seeing a cheaper negative-weight route later). DAG shortest paths is the fastest of all but only applies when the input truly has no cycles. Choosing among them is a matter of matching the algorithm's assumptions to the graph at hand, not always reaching for the most general tool.

Worked example

Consider a small currency-arbitrage-style graph with weights representing costs (negative weights allowed), source ss: ``` Vertices: s, A, B, C Edges (u, v, weight): (s, A, 6) (s, B, 4) (B, A, -3) (A, C, 2) (B, C, 5) (C, A, 1) ``` There is no negative cycle here (A→C→AA \to C \to A has weight 2+1=3≥02 + 1 = 3 \ge 0). Trace Bellman-Ford, relaxing edges in the fixed order listed above, for ∣V∣−1=3|V| - 1 = 3 rounds.

Initialization: d[s]=0, d[A]=d[B]=d[C]=∞d[s]=0,\ d[A]=d[B]=d[C]=\infty, all π=NIL\pi = \text{NIL}.

RoundRelax (s,A,6)(s,A,6)Relax (s,B,4)(s,B,4)Relax (B,A,−3)(B,A,-3)Relax (A,C,2)(A,C,2)Relax (B,C,5)(B,C,5)Relax (C,A,1)(C,A,1)
1d[A]=6,π[A]=sd[A]=6,\pi[A]=sd[B]=4,π[B]=sd[B]=4,\pi[B]=sd[A]=1,π[A]=Bd[A]=1,\pi[A]=B (since 4−3=1<64-3=1<6)d[C]=3,π[C]=Ad[C]=3,\pi[C]=Ano change (4+5=9≥34+5=9 \ge 3)no change (3+1=4≥13+1=4 \ge 1)
2no change (0+6=6≥10+6=6\ge1)no changeno change (4−3=1≥14-3=1\ge1)no change (1+2=3≥31+2=3\ge3)no changeno change (3+1=4≥13+1=4\ge1)
3no changeno changeno changeno changeno changeno change

Distances stabilize after round 1 in this example (subsequent rounds confirm no further improvement, satisfying the theoretical guarantee of at most ∣V∣−1=3|V|-1=3 rounds). Final result:

d[s]=0,d[B]=4,d[A]=1,d[C]=3d[s]=0,\quad d[B]=4,\quad d[A]=1,\quad d[C]=3
π[A]=B,π[B]=s,π[C]=A.\pi[A]=B,\quad \pi[B]=s,\quad \pi[C]=A.

Reconstructing the shortest path to CC: follow π[C]=A→π[A]=B→π[B]=s\pi[C]=A \to \pi[A]=B \to \pi[B]=s, giving path s→B→A→Cs \to B \to A \to C with weight 4+(−3)+2=34 + (-3) + 2 = 3, matching d[C]d[C]. Note this path has more edges than the direct-looking s→A→Cs \to A \to C (weight 6+2=86+2=8), illustrating why "shortest" means minimum total weight, not minimum edge count — the crucial difference from unweighted BFS shortest paths.

Negative-cycle case. Now add edge (C,B,−10)(C, B, -10) to the same graph. The cycle B→A→C→BB \to A \to C \to B has weight −3+2+(−10)=−11<0-3 + 2 + (-10) = -11 < 0 and is reachable from ss. Running the final check pass after ∣V∣−1|V|-1 rounds, edge (C,B,−10)(C,B,-10) will still satisfy d[C]+(−10)<d[B]d[C] + (-10) < d[B] no matter how the rounds settle, so Bellman-Ford correctly reports "negative-weight cycle reachable from ss" instead of returning a finite (and wrong) distance array.

Common pitfalls & traps

PitfallWhy it happensConsequence
Confusing "fewest edges" with "least weight"Habit carried over from BFS on unweighted graphsWrong shortest path reported whenever weights vary
Running Dijkstra with negative edge weightsAssuming Dijkstra is "just a faster Bellman-Ford"Silently wrong finalized distances — no error is raised
Off-by-one in the round countForgetting the bound is ∣V∣−1|V|-1 rounds, not ∣V∣|V| or ∣E∣|E|Either wasted work or (worse) stopping one round too early
Treating ∞−w\infty - w as a valid numberNaively computing d[u]+w(u,v)d[u] + w(u,v) when d[u]=∞d[u] = \inftyArithmetic overflow or spurious "improvement" from unreachable vertices
Ignoring vertices unreachable from ssForgetting that d[v]=∞d[v]=\infty is a legitimate final answerReporting a path that does not exist, or crashing on `NIL` predecessor
Missing the negative-cycle check entirelyTreating Bellman-Ford as "just relax ∣V∣−1|V|-1 times"Returns a finite but meaningless distance for affected vertices

Pioneers & history

The shortest-paths framework, though presented here as one unified idea, emerged from several independent lines of research in operations research and early computer science, each solving a version of the routing and scheduling problems that motivate this article.

Origins · 1 minRichard E. Bellman and how this was discovered
RE
Richard E. Bellman
1920–1984 · Applied mathematician, RAND Corporation

Bellman developed dynamic programming as a general method for multistage decision problems, and the shortest-path relaxation algorithm bearing his name (with Lester Ford Jr.) emerged directly from this framework — treating the shortest path to a vertex as an optimal substructure over the number of edges used, exactly the recursive idea generalized in dynamic programming.

History
1956
Lester Ford Jr. describes an algorithm for shortest paths in networks with arbitrary edge weights, later formalized with Bellman's dynamic programming lens as the Bellman-Ford algorithm.
1959
Edsger W. Dijkstra publishes his greedy shortest-path algorithm for non-negative weights, originally devised as a demonstration for a new computer.
1962
Robert Floyd and Stephen Warshall's related all-pairs shortest-path techniques appear, showing the deep connection between shortest paths and dynamic programming over path length.
1970
s: Shortest-paths algorithms become foundational in the ARPANET-era routing protocols, where negative-cycle-free assumptions are baked into distance-vector routing.
2000
s–present: Shortest-paths frameworks underlie modern route planners, network routing protocols, and graph databases operating at planetary scale.

Where this matters

Every internet router running a distance-vector routing protocol (such as early RIP) is essentially executing a distributed, iterative relaxation process resembling Bellman-Ford, where each router refines its estimate of the shortest path to every destination based on advertisements from its neighbors — and network engineers must carefully avoid configurations that create negative "cost" cycles (or the routing analogue, count-to-infinity problems). Modern GPS and mapping services solve enormous SSSP instances continuously, using specialized variants (bidirectional search, contraction hierarchies, A*) that are all still built on the relaxation invariant described here. Financial arbitrage detection literally uses the negative-cycle detection in this framework: taking the negative logarithm of currency exchange rates turns "find a profitable trading loop" into "find a negative-weight cycle," directly reusing Bellman-Ford's final check pass as a fraud- or opportunity-detection tool. Compilers and build systems that must schedule tasks with weighted dependencies, and robotics motion planners computing cost-optimal paths through a configuration space, rely on the same dd/π\pi output contract described in this article — regardless of which specific relaxation-ordering algorithm produces it.

:::quiz[What does the predecessor array π[v]\pi[v] represent once an SSSP algorithm terminates correctly?]

  • The vertex with the globally largest distance from the source
  • The immediate predecessor of vv on a shortest path from the source ✓
  • A vertex chosen arbitrarily during graph traversal
  • The vertex farthest from vv in the graph

Ask the tutor: How does the choice of relaxation order in Dijkstra's algorithm let it avoid ever needing to "undo" a finalized distance, and why does that same guarantee break down with negative edge weights?

What is the difference between the SSSP problem and the all-pairs shortest paths problem?

Single-source shortest paths fixes one source vertex ss and asks for the shortest distance and path from ss to every other vertex, producing one dd array and one π\pi array. All-pairs shortest paths asks for the shortest distance between every pair of vertices, effectively running an SSSP computation from every possible source. One brute-force way to solve all-pairs is to run an SSSP algorithm ∣V∣|V| times, once per source, giving O(∣V∣2⋅∣E∣)O(|V|^2 \cdot |E|) with Bellman-Ford; specialized algorithms like Floyd-Warshall or Johnson's algorithm (which itself reuses Bellman-Ford for reweighting) do better by sharing work across sources.

Can a graph have a shortest path that is not simple, i.e., that repeats a vertex?

No, as long as the graph has no negative-weight cycles reachable from the source. If a path revisits a vertex, it contains a cycle; since all cycle weights are non-negative in this case, removing the cycle from the path either keeps the weight the same or makes it smaller, and it always makes the path shorter in the sense of fewer edges. So there is always a simple path achieving the minimum weight, which is exactly why the ∣V∣−1|V|-1 round bound in Bellman-Ford is valid. If negative cycles are reachable, however, "shortest path" is not even well defined, since looping the cycle repeatedly decreases the total weight without bound.

Why does the framework initialize all non-source distances to infinity instead of zero?

Infinity represents "no path found yet," which is the honest starting assumption before any edges have been examined. If distances started at zero, the algorithm would falsely believe every vertex is already reachable at zero cost, and relaxation (which only ever decreases distances) could never correct that false belief, since d[u]+w(u,v)d[u]+w(u,v) would rarely be less than an already-optimistic zero. Starting from infinity guarantees the monotone-decrease invariant is meaningful: every finite value that d[v]d[v] ever takes corresponds to the weight of some real path from ss to vv.

How does this framework relate to BFS on unweighted graphs?

BFS is the special case of this framework where every edge has weight 11 and relaxation is done in the exact order a queue produces (vertices in increasing order of edge-count distance from the source). BFS shortest paths on unweighted graphs, a prerequisite for this material, already introduced the idea of a distance array and a predecessor structure — SSSP simply generalizes "distance" from "number of edges" to "sum of arbitrary edge weights," which is why BFS can be seen as the unweighted degenerate instance of the more general relaxation framework.

If my graph has no negative weights at all, do I still need to worry about negative cycles?

No — if every edge weight is non-negative, negative-weight cycles are structurally impossible, since any cycle's total weight is a sum of non-negative numbers and therefore itself non-negative. In that setting you can safely use Dijkstra's algorithm, which is asymptotically faster than Bellman-Ford, without ever needing the negative-cycle detection pass. The detection machinery described in this article specifically matters when some edge weights are allowed to be negative, even if most of the graph's edges are not.

What exactly does "detecting" a negative cycle let you do, since the shortest paths themselves are undefined?

Detection lets the algorithm fail gracefully and honestly rather than returning a plausible-looking but meaningless number. In practice, detecting a negative cycle is often the actual goal of running the algorithm — for example, in currency arbitrage detection, the presence of a negative cycle (after transforming exchange rates via logarithms) is the answer being sought, not an error condition to route around. So the detection step in Bellman-Ford is not just error handling; it doubles as a general-purpose negative-cycle-existence test for any weighted directed graph.

Does the choice of edge-relaxation order ever change the final answer, assuming the algorithm terminates correctly?

No — as long as the algorithm relaxes edges enough times and in an order compatible with the graph's structure (all edges eventually processed, with dependencies respected where required, as in DAG order or Dijkstra's greedy order), the final dd and π\pi arrays converge to the same true values δ(s,v)\delta(s,v) and a valid shortest-paths tree. This is because the upper-bound invariant guarantees d[v]d[v] can never go below δ(s,v)\delta(s,v), and any complete, sufficiently thorough relaxation order eventually forces d[v]d[v] down to exactly δ(s,v)\delta(s,v). Different orders only affect how many relaxation steps are needed, i.e., the running time, not the correctness of the final result.

Before this

Where this leads