A Bit of History
Few algorithms have as tangled a discovery history. French mathematician Bernard Roy published essentially the same triple-loop technique in 1959 for computing transitive closure. Three years later, in 1962, Stephen Warshall independently published a clean Boolean version for the same transitive-closure problem, and in the very same year, Robert Floyd independently adapted the identical loop structure to compute shortest-path distances rather than mere reachability. Floyd's short paper, titled simply "Algorithm 97: Shortest Path," is barely a page long — one of the most compact and consequential algorithm descriptions ever published. Because of this tangled parallel discovery, you'll see the algorithm called "Floyd-Warshall," "Roy-Warshall," or occasionally "Roy-Floyd-Warshall" depending on the textbook.
Working Principle
Floyd-Warshall computes all \(V^2\) shortest distances via dynamic programming over allowed intermediate vertices. Define \(d_k[i][j]\) as the shortest distance from \(i\) to \(j\) using only vertices \(\{1, \dots, k\}\) as intermediate stops. The recurrence considers exactly one question per step: does routing through vertex \(k\) help?
$$d_k[i][j] = \min\big(d_{k-1}[i][j],\ d_{k-1}[i][k] + d_{k-1}[k][j]\big)$$
Remarkably, the entire 3-dimensional DP can be computed in place using a single 2D matrix, since each update only ever reads values that are still valid for the current \(k\):
def floyd_warshall_pseudocode(dist):
"""dist: V x V matrix, dist[i][j] = edge weight or infinity if no edge, 0 on diagonal."""
n = len(dist)
for k in range(n):
for i in range(n):
for j in range(n):
if dist[i][k] + dist[k][j] < dist[i][j]:
dist[i][j] = dist[i][k] + dist[k][j] # route i -> k -> j is shorter
return dist
Worked Example
A 3-vertex graph: \(A \to B\) (3), \(B \to C\) (1), \(A \to C\) (10).
flowchart LR
A -->|3| B
B -->|1| C
A -.->|"10 (direct)"| C
Before considering \(B\) as an intermediate, \(d[A][C] = 10\) (the only known route). When \(k = B\), the algorithm checks: is \(d[A][B] + d[B][C] = 3 + 1 = 4\) less than \(d[A][C] = 10\)? Yes — update \(d[A][C]\) to 4. This single check, repeated for every \((i,j)\) pair and every intermediate \(k\), is the entire algorithm.
Why the DP Recurrence Works
This is induction on \(k\), in the style of Part 1. Claim: after processing intermediate vertex \(k\), \(d[i][j]\) equals the true shortest distance from \(i\) to \(j\) using only \(\{1,\ldots,k\}\) as intermediates. Base case (\(k=0\), no intermediates allowed): \(d[i][j]\) is just the direct edge weight (or \(\infty\)), correctly initialized. Inductive step: the true shortest path using vertices up to \(k\) either avoids \(k\) entirely (so the Part \(k-1\) value is already correct and unchanged) or passes through \(k\) exactly once (since revisiting \(k\) would only add cost, assuming no negative cycles) — splitting it into a best path to \(k\) and a best path from \(k\), both using only \(\{1,\ldots,k-1\}\) as intermediates, both already correct by the inductive hypothesis. The recurrence takes the minimum of these two cases, which is exactly correct.
Detecting Negative Cycles
After running the algorithm, check the diagonal: if any \(d[i][i] < 0\), a negative cycle passes through \(i\) — a vertex "improving its distance to itself" can only mean a cycle with negative total weight exists. This mirrors Bellman-Ford's extra-round check, adapted to the all-pairs setting.
Complexity Analysis
Three nested loops over all \(V\) vertices:
$$\text{Time: } O(V^3) \qquad \text{Space: } O(V^2)$$
No dependency on \(E\) at all — Floyd-Warshall's cost is identical whether the graph is sparse or dense, which is exactly why it's the wrong choice for large sparse graphs (Johnson's algorithm, Part 10, wins there) but an excellent, simple choice for dense or small graphs where \(V\) is modest.
Implementation
Real-World Applications
Network Latency Matrices and Board-Game AI
Network engineers use Floyd-Warshall to precompute an all-pairs latency matrix for a modestly sized set of data centers or routing hubs — small enough that \(O(V^3)\) is cheap, and useful enough that every pairwise route is needed simultaneously. The same algorithm (in its Boolean, Warshall-only form) computes reachability matrices for game-tree analysis and compiler dataflow analysis, where "can state X reach state Y" matters more than exact distance.
Exercises
- Run Floyd-Warshall by hand on a 4-vertex graph of your choosing, tracking the distance matrix after each value of \(k\).
- Modify the Python implementation to also reconstruct the actual shortest path (not just its length) using a "next hop" matrix updated alongside `dist`.
- Explain why Floyd-Warshall's in-place update (a single matrix, not three separate \(d_{k-1}\), \(d_k\) copies) is still correct, even though it appears to mix values from different \(k\) at first glance.
- Challenge: Implement Warshall's Boolean transitive-closure variant (replace min/plus with OR/AND) and verify it correctly computes reachability on a directed graph with a cycle.
Limitations
O(V³) Doesn't Scale to Large Sparse Graphs
On a graph with 100,000 vertices, \(V^3\) is far beyond feasible regardless of how few edges exist — Floyd-Warshall is a poor choice whenever \(V\) is large, sparse or not. Use Dijkstra from every vertex, or Johnson's algorithm (Part 10) if negative weights are possible, once \(V\) exceeds roughly a few thousand.