Purpose: Find the shortest path between every pair of vertices in a sparse graph with possibly negative edge weights (but no negative cycles), in O(V² log V + VE) time — faster than Floyd-Warshall on sparse graphs.
Algorithm
- Add a virtual source vertex connected to every other vertex with edge weight 0.
- Run Bellman-Ford once from this virtual source to compute
h[v], the shortest distance from the virtual source to every vertexv. This also detects negative-weight cycles — abort if one exists. - Reweight every original edge
(u, v, w)usingw' = w + h[u] - h[v]. This makes all edge weights non-negative while preserving which path is shortest. - Remove the virtual source. Run Dijkstra from every vertex
uon the reweighted graph to getd'(u, v), the shortest reweighted distance to every other vertex. - Convert back to real distances:
d(u, v) = d'(u, v) - h[u] + h[v].
Code
vector<vector<int>> johnsonsAlgorithm(int n, vector<array<int,3>>& edges) {
vector<array<int,3>> bfEdges = edges;
for (int v = 0; v < n; v++) bfEdges.push_back({n, v, 0});
vector<int> h(n + 1, INT_MAX);
h[n] = 0;
for (int i = 0; i < n; i++) {
for (auto& [u, v, w] : bfEdges) {
if (h[u] != INT_MAX && h[u] + w < h[v]) h[v] = h[u] + w;
}
}
for (auto& [u, v, w] : bfEdges) {
if (h[u] != INT_MAX && h[u] + w < h[v]) throw runtime_error("Negative cycle");
}
vector<vector<pair<int,int>>> adj(n);
for (auto& [u, v, w] : edges) {
adj[u].push_back({v, w + h[u] - h[v]});
}
vector<vector<int>> dist(n, vector<int>(n, INT_MAX));
for (int src = 0; src < n; src++) {
vector<int> d(n, INT_MAX);
d[src] = 0;
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
pq.push({0, src});
while (!pq.empty()) {
auto [du, u] = pq.top(); pq.pop();
if (du > d[u]) continue;
for (auto [v, w] : adj[u]) {
if (d[u] + w < d[v]) {
d[v] = d[u] + w;
pq.push({d[v], v});
}
}
}
for (int dst = 0; dst < n; dst++) {
if (d[dst] != INT_MAX) dist[src][dst] = d[dst] - h[src] + h[dst];
}
}
return dist;
}Paradigm
Transform and Conquer. The core idea is instance simplification: reweight the graph via h[] to eliminate negative edges, transforming a hard instance (negative weights, Dijkstra unusable) into an easier equivalent one (non-negative weights) solvable with a faster algorithm.
Complexity
- Time: O(V² log V + VE) — one Bellman-Ford run (O(VE)) plus V runs of Dijkstra (O(V log V + E) each)
- Space: O(V²) for the output distance matrix
Proof of Correctness
Part 1 — Reweighting preserves shortest paths: For any path P from u to v passing through vertices u = p0, p1, ..., pk = v, its reweighted length telescopes:
Σ w'(pi, pi+1) = Σ [w(pi, pi+1) + h[pi] - h[pi+1]] = Σ w(pi, pi+1) + h[u] - h[v]
Every path between the same pair (u, v) shifts by the exact same constant h[u] - h[v], so the relative order of path lengths — and therefore which path is shortest — is unchanged. This means d(u,v) = d'(u,v) - h[u] + h[v] recovers the true shortest distance.
Part 2 — Reweighted edges are non-negative: Since h[v] is the shortest distance from the virtual source to v, and the virtual source connects to every vertex with weight 0, the shortest-path property guarantees the triangle inequality h[v] ≤ h[u] + w(u,v) for every edge (u,v,w). Rearranging gives w(u,v) + h[u] - h[v] ≥ 0, i.e., every reweighted edge w' is non-negative — exactly what Dijkstra requires to run correctly.
Part 3 — Negative cycle detection: Bellman-Ford’s own correctness guarantees it detects any negative cycle reachable from the virtual source, which — since the virtual source connects to all vertices — covers any negative cycle in the graph. ∎
Variants / Use Cases
- Sparse graph all-pairs shortest paths → preferred over Floyd-Warshall when
E << V², since Johnson’s is faster on sparse graphs - Currency arbitrage detection across many pairs → reweighting + Dijkstra scales better than repeated Bellman-Ford
- Re-run Dijkstra incrementally → if only a few edges change, only affected sources need re-running instead of recomputing
h[]and all pairs from scratch - Distributed/road network routing → used in systems needing many-to-many shortest paths where graphs are large but sparse
- A with landmarks (ALT algorithm)* → related reweighting/potential-function idea, using precomputed distances as heuristics instead of correctness-preserving transforms
Built on: Bellman-Ford + Dijkstra.