CO2 Topic 15 of 34
Floyd–Warshall all-pairs shortest paths
Shortest distances between every pair of vertices with three nested loops: allow one more intermediate vertex at a time, and update the whole distance matrix.
You will be able to
- State the dynamic-programming recurrence D_k[i][j] = min(D_{k−1}[i][j], D_{k−1}[i][k] + D_{k−1}[k][j])
- Compute the matrices D_0, D_1, …, D_n by hand for a small graph
- Detect negative cycles from the diagonal, and compute transitive closure (Warshall)
- Compare Floyd–Warshall with running Dijkstra or Bellman–Ford from every vertex
Read
- Textbook 1, §25.2 The Floyd-Warshall algorithm
- Textbook 2, §5.3 All-pairs shortest paths
- Textbook 5, Ch. on dynamic programming (all-pairs shortest paths)
Watch
Intuition
You want a table of road distances between every pair of cities. Floyd–Warshall builds it gradually:
- D₀: only direct roads allowed.
- D₁: routes may also pass through city 1.
- D₂: routes may pass through cities 1 and 2.
- … Dₙ: routes may pass through any city — the final answer.
Going from D_{k−1} to D_k asks one question for every pair (i, j): is it shorter to go via k?
D_k[i][j] = min( D_{k−1}[i][j], D_{k−1}[i][k] + D_{k−1}[k][j] )
digraph {
node [shape=circle, fontname="Inter"]; edge [fontname="JetBrains Mono"];
1 -> 2 [label="3"]; 1 -> 4 [label="7"]; 2 -> 1 [label="8"]; 2 -> 3 [label="2"];
3 -> 1 [label="5"]; 3 -> 4 [label="1"]; 4 -> 1 [label="2"];
}
How it works
Matrices for the graph above (row = from, column = to; bold = changed in that step):
D₀ (direct edges)
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | 3 | ∞ | 7 |
| 2 | 8 | 0 | 2 | ∞ |
| 3 | 5 | ∞ | 0 | 1 |
| 4 | 2 | ∞ | ∞ | 0 |
D₁ (via 1): 2→4 = 8 + 7 = 15, 3→2 = 5 + 3 = 8, 4→2 = 2 + 3 = 5.
D₂ (via 1, 2): 1→3 = 3 + 2 = 5, 4→3 = 5 + 2 = 7.
D₃ (via 1, 2, 3): 1→4 = 5 + 1 = 6, 2→1 = 2 + 5 = 7, 2→4 = 2 + 1 = 3.
D₄ — final (via any vertex): 2→1 = 3 + 2 = 5, 3→1 = 1 + 2 = 3, 3→2 = 1 + 5 = 6.
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | 3 | 5 | 6 |
| 2 | 5 | 0 | 2 | 3 |
| 3 | 3 | 6 | 0 | 1 |
| 4 | 2 | 5 | 7 | 0 |
Negative cycles: if some diagonal entry D[i][i] becomes negative, vertex i lies on a negative cycle.
Transitive closure (Warshall's algorithm): replace min/+ with or/and to answer "is there any path from i to j?":
R_k[i][j] = R_{k−1}[i][j] or (R_{k−1}[i][k] and R_{k−1}[k][j])
Python implementation
INF = float("inf")
def floyd_warshall(n, edges):
"""Vertices 0..n-1; edges: list of (u, v, w). Returns the distance matrix."""
d = [[0 if i == j else INF for j in range(n)] for i in range(n)]
for u, v, w in edges:
d[u][v] = min(d[u][v], w)
for k in range(n): # allow k as an intermediate vertex
for i in range(n):
for j in range(n):
if d[i][k] + d[k][j] < d[i][j]:
d[i][j] = d[i][k] + d[k][j]
if any(d[i][i] < 0 for i in range(n)):
raise ValueError("negative-weight cycle")
return d
def transitive_closure(n, edges):
r = [[i == j for j in range(n)] for i in range(n)]
for u, v in edges:
r[u][v] = True
for k in range(n):
for i in range(n):
for j in range(n):
r[i][j] = r[i][j] or (r[i][k] and r[k][j])
return r
if __name__ == "__main__":
edges = [(0, 1, 3), (0, 3, 7), (1, 0, 8), (1, 2, 2), (2, 0, 5), (2, 3, 1), (3, 0, 2)]
for row in floyd_warshall(4, edges):
print(row)
# [0, 3, 5, 6]
# [5, 0, 2, 3]
# [3, 6, 0, 1]
# [2, 5, 7, 0]
print(transitive_closure(3, [(0, 1), (1, 2)])[0]) # [True, True, True]
(Vertices 1–4 of the drawing are 0–3 in the code.)
Complexity
| Approach for all pairs | Time | Negative edges |
|---|---|---|
| Floyd–Warshall | Θ(V³), Θ(V²) space | ✓ (detects negative cycles) |
| Dijkstra from every vertex (binary heap) | O(V·(V + E) log V) | ✗ |
| Bellman–Ford from every vertex | O(V²·E) | ✓ |
Floyd–Warshall is the simplest and best for dense graphs; for large sparse graphs with non-negative weights, V runs of Dijkstra are faster.
Common mistakes
- Putting k in the inner loop. The loop over the intermediate vertex k must be the outermost — otherwise you use paths through k before they are computed.
- Initialising the diagonal to ∞. D[i][i] must start at 0.
- Overflow with ∞ + ∞ in languages with fixed-size integers — use a large constant and check before adding. (Python's
float('inf')is safe.) - Reading a negative diagonal as a distance. It means a negative cycle, not "−5 from i to itself".
Problems
- Compute D₁ … D₄ for the graph with edges 1→2 (4), 1→3 (11), 2→3 (−2), 2→4 (6), 3→4 (3), 3→1 (7), 4→1 (1).
- Lab: extend
floyd_warshallto return anextmatrix and print the actual shortest path between any two vertices. - Lab: a small network's links are given as (router, router, delay). Print the full delay table and the router whose worst delay to any other router is smallest (the best place for a server).
- Use Warshall's algorithm to find which pairs of courses are related by a chain of prerequisites.
- Why does Floyd–Warshall still work when edge weights are negative but no cycle is negative?
Quiz
GATE-style practice
Questions in the GATE CSE formats: MCQ (one correct option), MSQ (one or more correct options) and NAT (type a number). Each comes with a full worked solution. These are original practice questions, not past papers; for the real thing, see the official GATE site and its previous question papers.