A Bit of History
Donald B. Johnson published “Efficient Algorithms for Shortest Paths in Sparse Networks” in 1977. The algorithm fills an important gap between Floyd–Warshall, whose $O(V^3)$ running time ignores sparsity, and Dijkstra's algorithm, which exploits sparse adjacency lists but requires nonnegative weights.
Johnson's contribution is a composition of ideas: Bellman–Ford supplies a feasible potential function, reduced costs make Dijkstra legal, and a final algebraic correction restores the original distances.
The Sparse All-Pairs Shortest-Path Gap
Given a directed weighted graph $G=(V,E)$, all-pairs shortest paths asks for $\delta(s,t)$ for every ordered pair of vertices. Negative edges are allowed, but a reachable negative cycle makes some distances unbounded below: walking around that cycle repeatedly keeps reducing the path cost.
Fast but restricted
Adjacency lists and a heap are efficient on sparse graphs, but a negative edge breaks the greedy settled-distance argument.
General but repeated
It handles negative edges and detects negative cycles, but running it from every source costs $O(V^2E)$.
Transform once
One Bellman–Ford pass makes every later source search compatible with Dijkstra.
The goal is not to preserve each edge's weight. It is to preserve the ordering of all paths that share the same start and end.
The Four-Stage Pipeline
- Make every vertex reachable. Add a temporary source $q$ with a zero-weight edge $q\to v$ to every original vertex.
- Compute potentials. Run Bellman–Ford from $q$. Its distance $h(v)=\delta(q,v)$ becomes the potential of $v$. A further relaxation means a negative cycle exists, so Johnson must stop.
- Build reduced weights. Replace every edge weight by $w'(u,v)=w(u,v)+h(u)-h(v)$. These values are nonnegative.
- Search and restore. Run Dijkstra from every original source with $w'$, then convert finite answers back using $\delta(s,t)=\delta'(s,t)-h(s)+h(t)$.
Implementations often avoid materializing $q$: initializing every potential to zero has the same effect as relaxing all zero-weight edges from the super-source once.
Potentials Turn Negative Edges Nonnegative
For the five-vertex example used in the implementations, Bellman–Ford computes $h=[0,-1,-5,0,-4]$. The potential is not an answer to an original source query; it is a globally consistent price adjustment. Four representative edges show how the adjustment works:
Why Must Every Reweighted Edge Be Nonnegative?
Because $h(v)$ is a shortest distance from $q$, taking a shortest route to $u$ and then edge $(u,v)$ cannot beat the optimum to $v$:
$$h(v)\le h(u)+w(u,v).$$
Rearranging gives $w(u,v)+h(u)-h(v)\ge0$, which is exactly $w'(u,v)\ge0$. This inequality is the bridge between Bellman–Ford and Dijkstra: Bellman–Ford certifies a potential that makes every reduced cost feasible.
Why Reweighting Preserves Shortest Paths
For a path $P=v_0\to v_1\to\cdots\to v_k$, sum the reweighted edges:
$$w'(P)=\sum_{i=1}^{k}\left[w(v_{i-1},v_i)+h(v_{i-1})-h(v_i)\right]=w(P)+h(v_0)-h(v_k).$$
Every intermediate potential appears once positively and once negatively, so it cancels. All paths with the same endpoints receive the same correction $h(v_0)-h(v_k)$. Their absolute costs change, but their ordering does not.
Why a Negative Cycle Stops the Algorithm
If Bellman–Ford can still relax an edge after the normal passes, some negative cycle is reachable from $q$. Because $q$ reaches every original vertex, that detects a negative cycle anywhere in the graph. For pairs that can enter and later leave such a cycle, there is no finite shortest distance: another lap always makes the walk cheaper.
Not Merely a Dijkstra Limitation
No all-pairs algorithm can return ordinary finite shortest distances for every pair in this situation. Johnson therefore rejects the graph before reweighting. If the actual cycle is needed, store predecessors during Bellman–Ford and reconstruct it.
Complexity Analysis
| Stage | Binary-heap implementation | Purpose |
|---|---|---|
| Bellman–Ford | $O(VE)$ | Compute potentials and detect any negative cycle. |
| Reweighting | $O(E)$ | Build a nonnegative adjacency list. |
| $V$ Dijkstra runs | $O\!\left(V(E+V)\log V\right)$ | Compute one distance row per source. |
| Distance matrix | $O(V^2)$ space | Store all ordered-pair answers; streaming rows can reduce output memory. |
On the usual sparse connected case $E=\Omega(V)$, the binary-heap total is commonly summarized as $O(VE\log V)$. With Fibonacci heaps, the Dijkstra portion becomes $O(VE+V^2\log V)$, matching the bound often quoted for Johnson's original analysis. Floyd–Warshall remains $O(V^3)$ regardless of $E$ and may be preferable for dense graphs or compact matrix-based implementations.
Implementation
All three versions use wide integer distances, validate endpoints, stop Bellman–Ford early when stable, preserve infinity for unreachable pairs, and return both the distance matrix and potentials. A null result indicates a negative cycle.
Choosing an All-Pairs Algorithm
| Method | Negative edges | Negative-cycle check | Typical time | Best fit |
|---|---|---|---|---|
| Repeated Dijkstra | No | No | $O(V(E+V)\log V)$ | Sparse graph with nonnegative weights |
| Johnson | Yes | Yes | $O(VE\log V)$ with binary heaps on typical sparse graphs | Sparse directed graph with negative edges but no negative cycle |
| Floyd–Warshall | Yes | Yes, via negative diagonal entries | $O(V^3)$ | Dense graphs, simple matrix code, or modest $V$ |
| Repeated Bellman–Ford | Yes | Yes | $O(V^2E)$ | Simple baseline; rarely the fastest APSP choice |
If only a handful of sources matter, running a suitable single-source algorithm for those sources is often better than materializing a full $V\times V$ matrix. Johnson is specifically an all-pairs strategy.
Where Johnson's Structure Helps
Sparse Route Tables
Compute directed route costs between every pair when adjustments or credits can make some edges negative.
Dependency Analysis
Answer many influence or transition-cost queries in a sparse directed dependency network.
Distance Precomputation
Build an exact distance oracle for a moderate sparse graph when repeated queries justify $O(V^2)$ output.
Arbitrage Screening
After negative-log conversion, Bellman–Ford's failure can flag a negative cycle; cycle reconstruction needs predecessors.
Reduced-Cost Intuition
The potential transformation mirrors reduced costs used throughout optimization and min-cost-flow algorithms.
Row Streaming
If consumers process one source at a time, Dijkstra rows can be emitted without retaining the full matrix.
Pitfalls and Edge Cases
| Mistake | Consequence | Robust treatment |
|---|---|---|
| Running Dijkstra on original negative weights | Settled vertices may later receive shorter paths. | Run it only on the nonnegative reduced graph. |
| Forgetting the super-source | A negative cycle in another component may be missed. | Connect $q$ to every vertex, or initialize every $h(v)$ to zero. |
| Using the wrong restore sign | Every finite answer is shifted incorrectly. | Use $d=d'-h(s)+h(t)$. |
| Converting infinity algebraically | An unreachable pair becomes a huge or nonsensical number. | Restore only finite Dijkstra distances. |
| Using 32-bit path totals | Long paths overflow even when each edge fits. | Use wide integers and leave headroom below the infinity sentinel. |
| Confusing directed and undirected negative edges | One negative undirected edge creates a two-edge negative cycle. | Johnson is mainly meaningful for directed graphs when negative edges exist. |
| Always storing the matrix | $O(V^2)$ output dominates memory. | Stream or consume one source row when the API permits. |
Directedness Matters
In an undirected representation, an edge of weight $-2$ means both $u\to v$ and $v\to u$ have weight $-2$, immediately producing a cycle of total $-4$. Negative-edge MST problems are different; Johnson solves directed shortest paths.
Exercises
Work with Potentials, Not Memorized Steps
- Reweight. Use $h=[0,-1,-5,0,-4]$ to calculate $w'$ for all nine sample edges and verify that none is negative.
- Telescope. Expand the potential terms on $0\to4\to3\to2\to1$ and cross out each intermediate cancellation.
- Restore. Dijkstra gives $d'(0,2)=2$. Recover the original $d(0,2)$ and check it against the path $0\to4\to3\to2$.
- Detect. Add edge $1\to0$ of weight $-4$. Identify a negative cycle and explain which Bellman–Ford inequality keeps improving.
- Disconnect. Add an isolated vertex 5. Predict its potential and all matrix entries involving it.
- Extend. Store a predecessor array for each Dijkstra run so a caller can reconstruct an actual shortest path.
Sanity checks: every diagonal distance is zero, every reduced edge is nonnegative, and reachable answers satisfy $d(s,v)\le d(s,u)+w(u,v)$.