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 and predecessor array . 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.
- 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 and predecessor array , refined via edge relaxation.
- Relaxation only ever lowers 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 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.
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 be a directed graph with a weight function assigning a real number to every edge (weights may be negative unless stated otherwise). For a path , its weight is
Given a source vertex , the shortest-path weight from to is defined as
The single-source shortest paths problem asks for for every vertex , 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 : an estimate of , initialized to for all and to for . A correct algorithm terminates with for every reachable .
- Predecessor array : the vertex immediately before on the current best-known path from , initialized to `NIL`. At termination, following pointers from any reachable back to traces out an actual shortest path.
The array implicitly defines a shortest-paths tree rooted at : a tree containing exactly the edges for every reachable , such that the unique tree path from to any is a shortest path in . 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 : check whether going through offers a cheaper route to 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, 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, for every vertex , and once it never changes again if no negative cycle affects . Relaxation only ever decreases , and it only decreases it to a value that corresponds to the weight of an actual path (namely, the path to via , plus edge ). Since is by definition the minimum over all such path weights, can never be forced below 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 reachable from with total weight , then any vertex on or reachable from has no well-defined shortest path: you can loop around arbitrarily many times, each time subtracting more weight, so
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 , then full rounds of relaxing every edge are always enough to reach the fixed point for all reachable . So if a further relaxation is still possible after round , the only explanation is a negative cycle.
Complexity & correctness
Correctness of the framework in general. The upper-bound invariant holds throughout execution by induction on the number of relaxations performed: initially and for all other ; each relaxation of edge sets , and by the inductive hypothesis , so the new is at least (since appending edge to any path to gives some path to , and is the minimum over all such paths). This shows never drops below the true shortest-path weight.
When does reach exactly? This requires that the specific algorithm's relaxation order eventually processes every edge of some shortest path to , in order from 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 is a shortest path and edges are relaxed in that relative order (not necessarily consecutively), then after all of them .
Why rounds suffice when there is no negative cycle. Any shortest path (if it exists and the graph has no negative cycle reachable from ) 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 vertices has at most edges. Bellman-Ford's outer loop relaxes all edges once per round; by induction, after round , every vertex whose shortest path uses at most edges has . After rounds, every simple shortest path — having at most edges — has been fully processed, so all reachable values are exact.
Why one more round detects a negative cycle. If, after rounds, some edge still admits , then was not the true fixed point — but we just argued rounds always suffice in the negative-cycle-free case. So the only way an -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 and can reach .
Complexity. Bellman-Ford performs rounds, each relaxing all edges, plus one final check pass:
Space is for the and arrays (plus 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 via topological order) and Dijkstra's algorithm (achieving 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 : ``` 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 ( has weight ). Trace Bellman-Ford, relaxing edges in the fixed order listed above, for rounds.
Initialization: , all .
| Round | Relax | Relax | Relax | Relax | Relax | Relax |
|---|---|---|---|---|---|---|
| 1 | (since ) | no change () | no change () | |||
| 2 | no change () | no change | no change () | no change () | no change | no change () |
| 3 | no change | no change | no change | no change | no change | no change |
Distances stabilize after round 1 in this example (subsequent rounds confirm no further improvement, satisfying the theoretical guarantee of at most rounds). Final result:
Reconstructing the shortest path to : follow , giving path with weight , matching . Note this path has more edges than the direct-looking (weight ), 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 to the same graph. The cycle has weight and is reachable from . Running the final check pass after rounds, edge will still satisfy no matter how the rounds settle, so Bellman-Ford correctly reports "negative-weight cycle reachable from " instead of returning a finite (and wrong) distance array.
Common pitfalls & traps
| Pitfall | Why it happens | Consequence |
|---|---|---|
| Confusing "fewest edges" with "least weight" | Habit carried over from BFS on unweighted graphs | Wrong shortest path reported whenever weights vary |
| Running Dijkstra with negative edge weights | Assuming Dijkstra is "just a faster Bellman-Ford" | Silently wrong finalized distances — no error is raised |
| Off-by-one in the round count | Forgetting the bound is rounds, not or | Either wasted work or (worse) stopping one round too early |
| Treating as a valid number | Naively computing when | Arithmetic overflow or spurious "improvement" from unreachable vertices |
| Ignoring vertices unreachable from | Forgetting that is a legitimate final answer | Reporting a path that does not exist, or crashing on `NIL` predecessor |
| Missing the negative-cycle check entirely | Treating Bellman-Ford as "just relax 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
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.
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 / output contract described in this article — regardless of which specific relaxation-ordering algorithm produces it.
:::quiz[What does the predecessor array represent once an SSSP algorithm terminates correctly?]
- The vertex with the globally largest distance from the source
- The immediate predecessor of on a shortest path from the source ✓
- A vertex chosen arbitrarily during graph traversal
- The vertex farthest from 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 and asks for the shortest distance and path from to every other vertex, producing one array and one 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 times, once per source, giving 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 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 would rarely be less than an already-optimistic zero. Starting from infinity guarantees the monotone-decrease invariant is meaningful: every finite value that ever takes corresponds to the weight of some real path from to .
How does this framework relate to BFS on unweighted graphs?
BFS is the special case of this framework where every edge has weight 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 and arrays converge to the same true values and a valid shortest-paths tree. This is because the upper-bound invariant guarantees can never go below , and any complete, sufficiently thorough relaxation order eventually forces down to exactly . Different orders only affect how many relaxation steps are needed, i.e., the running time, not the correctness of the final result.