Skip to content
Part IA Lent Term

Johnson's Algorithm

Overview

Johnson’s algorithm solves the all-pairs shortest paths (APSP) problem for sparse graphs with possibly negative edge weights. It runs Bellman-Ford once to compute a potential function hh, uses hh to reweight all edges to non-negative values, then runs Dijkstra ∣V∣|V| times. The original distances are recovered by subtracting the potential difference.

The key insight: adding a constant to every edge weight does not preserve shortest paths (longer paths accumulate more bias). Instead, the bias must depend on the endpoints: w^(u,v)=w(u,v)+h(u)−h(v)\hat{w}(u, v) = w(u, v) + h(u) - h(v).

Why Naive Reweighting Fails

Adding a fixed constant bb to every edge weight makes all edges non-negative, but changes which paths are shortest. If two paths from AA to BB have 2 edges and 5 edges respectively, the 5-edge path accumulates 5b5b vs 2b2b, so the ranking can change. Johnson’s vertex-potential reweighting avoids this.

The Reweighting Function

Given a function h:V→Rh: V \to \mathbb{R}, define the reweighted edge weight:

w^(u,v)=w(u,v)+h(u)−h(v)\hat{w}(u, v) = w(u, v) + h(u) - h(v)

Property 1 (shortest-path preservation): For any path p=v1→v2→⋯→vkp = v_1 \to v_2 \to \cdots \to v_k:

w^(p)=∑i=1k−1(w(vi,vi+1)+h(vi)−h(vi+1))=w(p)+h(v1)−h(vk)\hat{w}(p) = \sum_{i=1}^{k-1} (w(v_i, v_{i+1}) + h(v_i) - h(v_{i+1})) = w(p) + h(v_1) - h(v_k)

The terms telescope: h(vi)h(v_i) and −h(vi+1)-h(v_{i+1}) cancel for interior vertices. The difference h(v1)−h(vk)h(v_1) - h(v_k) depends only on the endpoints, not the path. Therefore w^\hat{w} preserves the relative ordering of paths between any fixed pair of vertices: a shortest path under ww is also a shortest path under w^\hat{w}.

Property 2 (non-negative weights): If h(v)=δ(s,v)h(v) = \delta(s, v) (the true shortest distance from some source ss), then by the triangle inequality δ(s,v)≤δ(s,u)+w(u,v)\delta(s, v) \le \delta(s, u) + w(u, v), so:

w^(u,v)=w(u,v)+h(u)−h(v)≥0\hat{w}(u, v) = w(u, v) + h(u) - h(v) \ge 0

Algorithm

  1. Augment GG to G′G': add a new vertex ss connected to every v∈Vv \in V with weight 0. G′G' has no new negative cycles.
  2. Run Bellman-Ford on G′G' from ss. If it detects a negative cycle, report failure. Otherwise, set h(v)=δ(s,v)h(v) = \delta(s, v) for all vv.
  3. Reweight every original edge: w^(u,v)=w(u,v)+h(u)−h(v)\hat{w}(u, v) = w(u, v) + h(u) - h(v). Now all edges have non-negative weight.
  4. For each vertex u∈Vu \in V, run Dijkstra on the reweighted graph to get δ^(u,v)\hat{\delta}(u, v) for all vv.
  5. Recover true distances: δ(u,v)=δ^(u,v)−h(u)+h(v)\delta(u, v) = \hat{\delta}(u, v) - h(u) + h(v).

Pseudocode:

JOHNSON(G, w):
  G' = (V ∪ {s}, E ∪ {(s,v) : v ∈ V}) with w(s,v)=0
  if BELLMAN-FORD(G', w, s) == FALSE:
    error("negative cycle")
  for each edge (u,v) in G.E:
    ŵ(u,v) = w(u,v) + u.d - v.d      // u.d = δ(s,u)
  D = new |V|×|V| matrix
  for each u in G.V:
    DIJKSTRA(G, ŵ, u)
    for each v in G.V:
      D[u][v] = v.d - u.h + v.h        // undo reweighting
  return D

Worked Example

Consider a graph with V={1,2,3}V = \{1,2,3\} and edges: 1→21 \to 2 (weight −2-2), 1→31 \to 3 (weight 44), 2→32 \to 3 (weight 11). This has a negative edge 1→21 \to 2 but no negative cycles.

Step 1: Augment with ss, edges s→1,s→2,s→3s \to 1, s \to 2, s \to 3 (weight 0).

Step 2: Bellman-Ford from ss gives h(1)=0h(1)=0, h(2)=0h(2)=0, h(3)=0h(3)=0 (there are no paths to improve, since ss edges are 0-weight). Actually, with the original edges present: h(1)=0h(1)=0, h(2)=−2h(2)=-2, h(3)=−1h(3)=-1 (path s→1→2→3s \to 1 \to 2 \to 3 costs 0−2+1=−10-2+1=-1, or s→1→3s \to 1 \to 3 costs 44).

Step 3: Reweight:

  • w^(1,2)=−2+0−(−2)=0\hat{w}(1,2) = -2 + 0 - (-2) = 0
  • w^(1,3)=4+0−(−1)=5\hat{w}(1,3) = 4 + 0 - (-1) = 5
  • w^(2,3)=1+(−2)−(−1)=0\hat{w}(2,3) = 1 + (-2) - (-1) = 0

All reweighted edges are non-negative.

Step 4: Run Dijkstra from 1: δ^(1,2)=0\hat{\delta}(1,2)=0, δ^(1,3)=0\hat{\delta}(1,3)=0 (via 1→2→31 \to 2 \to 3).

Step 5: Recover: δ(1,2)=0−0+(−2)=−2\delta(1,2) = 0 - 0 + (-2) = -2, δ(1,3)=0−0+(−1)=−1\delta(1,3) = 0 - 0 + (-1) = -1.

Results match the original graph (path 1→2→31 \to 2 \to 3 costs −2+1=−1-2+1=-1).

Running Time

  • Step 1: Θ(∣V∣)\Theta(|V|).
  • Step 2: Θ(∣V∣⋅∣E∣)\Theta(|V|\cdot|E|) (Bellman-Ford).
  • Step 3: Θ(∣E∣)\Theta(|E|).
  • Step 4: ∣V∣|V| calls to Dijkstra. With binary heap: O(∣V∣⋅(∣V∣+∣E∣)log⁡∣V∣)=O(∣V∣⋅∣E∣log⁡∣V∣)O(|V| \cdot (|V|+|E|)\log|V|) = O(|V| \cdot |E| \log |V|). With Fibonacci heap: O(∣V∣2log⁡∣V∣+∣V∣⋅∣E∣)O(|V|^2 \log |V| + |V| \cdot |E|).
  • Step 5: Θ(∣V∣2)\Theta(|V|^2).

Total: O(∣V∣2log⁡∣V∣+∣V∣⋅∣E∣)O(|V|^2 \log |V| + |V| \cdot |E|) with Fibonacci heap, or O(∣V∣⋅∣E∣log⁡∣V∣)O(|V| \cdot |E| \log |V|) with binary heap.

Comparison with Floyd-Warshall

AlgorithmTimeBest for
JohnsonO(V2log⁡V+VE)O(V^2 \log V + VE)Sparse graphs
Floyd-WarshallΘ(V3)\Theta(V^3)Dense graphs

Johnson wins when ∣E∣=o(∣V∣2)|E| = o(|V|^2). When ∣E∣=Θ(∣V∣2)|E| = \Theta(|V|^2), Floyd-Warshall’s simpler implementation and smaller constant factor make it preferable.

Summary

StepOperationTime
AugmentAdd dummy ssO(V)O(V)
Bellman-FordCompute h(v)h(v)O(VE)O(VE)
Reweightw^=w+h(u)−h(v)\hat{w} = w + h(u) - h(v)O(E)O(E)
Dijkstra × VVAll-pairsO(V⋅Elog⁡V)O(V \cdot E \log V)
Un-reweightD[u][v]+h(u)−h(v)D[u][v] + h(u) - h(v)O(V2)O(V^2)

Past paper questions: y2024p1q9 (reweighting, relaxed costs, opportunities).