Floyd-Warshall
Floyd-Warshall computes the shortest distance between every pair of vertices with three nested loops, by allowing one more intermediate vertex in each round; it handles negative edges and exposes negative cycles on the diagonal.
Learning goals
- Fill the distance matrix with the three loops in the right order
- State the recurrence in terms of allowed intermediate vertices
- Detect vertices on a negative cycle
- Compute the transitive closure with the same pattern
Prerequisites
Sometimes you need the answer for every pair: a distance table between cities, the graph's diameter, or which stations reach which. For a few hundred vertices a three-line algorithm is plenty.
All-pairs DP
Store the matrix d[i][j]: infinity at first, 0 on the diagonal, and the edge weight (the lightest if there are parallel edges) where an edge exists. For each vertex k in turn, and for every pair (i, j), try the route i → k → j: if d[i][k] + d[k][j] < d[i][j], update. The result is the distance for every pair.
It takes O(n³) time and O(n²) space regardless of the number of edges, so it suits dense graphs or small n. Step through the animation: each improved cell lights up, and row k and column k are marked as the new intermediate vertex.
1static long[][] floyd(int n, int[][] edges) { // edges {u, v, w}; null when a negative cycle exists2long INF = Long.MAX_VALUE / 4;3long[][] d = new long[n][n];4for (long[] r : d) Arrays.fill(r, INF);5for (int i = 0; i < n; i++) d[i][i] = 0;6for (int[] e : edges) d[e[0]][e[1]] = Math.min(d[e[0]][e[1]], e[2]);7for (int k = 0; k < n; k++) // k must be the outermost loop8for (int i = 0; i < n; i++)9for (int j = 0; j < n; j++)10if (d[i][k] < INF && d[k][j] < INF && d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];11for (int i = 0; i < n; i++) if (d[i][i] < 0) return null;12return d;13}
Floyd-Warshall: all-pairs shortest paths and transitive closure
Interactive · edit the inputgrid · 5 × 5
d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop.
1long[][] floydWarshall(int n, int[][] edges) { // edges: {u, v, w}; null when a negative cycle exists2long INF = Long.MAX_VALUE / 4;3long[][] d = new long[n][n];4for (long[] r : d) Arrays.fill(r, INF);5for (int i = 0; i < n; i++) d[i][i] = 0;6for (int[] e : edges) d[e[0]][e[1]] = Math.min(d[e[0]][e[1]], e[2]); // keep the lightest parallel edge7for (int k = 0; k < n; k++) // intermediate vertices allowed so far: 0..k8for (int i = 0; i < n; i++)9for (int j = 0; j < n; j++)10if (d[i][k] < INF && d[k][j] < INF && d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];11for (int i = 0; i < n; i++) if (d[i][i] < 0) return null; // a vertex that returns to itself with negative cost12return d;13}
Live state
n
4
Execution trace (dry run table)Show 11 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | n=4 | d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop. |
| 2 | L7 | k=0 | Round 0: going through 0 changes nothing. |
| 3 | L9 | k=1 i=0 j=2 d[i][j]=1 | Through 1: d[0][1] + d[1][2] = 3 + -2 = 1, better than the old d[0][2]. Update it. |
| 4 | L9 | k=1 i=3 j=2 d[i][j]=-1 | Through 1: d[3][1] + d[1][2] = 1 + -2 = -1, better than the old d[3][2]. Update it. |
| 5 | L7 | k=1 | Round 1 done: some distances improved by going through 1. Intermediate vertices 0..1 are now allowed. |
| 6 | L9 | k=2 i=0 j=3 d[i][j]=3 | Through 2: d[0][2] + d[2][3] = 1 + 2 = 3, better than the old d[0][3]. Update it. |
| 7 | L9 | k=2 i=1 j=3 d[i][j]=0 | Through 2: d[1][2] + d[2][3] = -2 + 2 = 0, better than the old d[1][3]. Update it. |
| 8 | L7 | k=2 | Round 2 done: some distances improved by going through 2. Intermediate vertices 0..2 are now allowed. |
| 9 | L9 | k=3 i=2 j=1 d[i][j]=3 | Through 3: d[2][3] + d[3][1] = 2 + 1 = 3, better than the old d[2][1]. Update it. |
| 10 | L7 | k=3 | Round 3 done: some distances improved by going through 3. Intermediate vertices 0..3 are now allowed. |
| 11 | L12 | return=[0, 3, 1, 3] | [-, 0, -2, 0] | [-, 3, 0, 2] | [-, 1, -1, 0] improvements=5 | The diagonal is all zeros, so there is no negative cycle. Row i holds the shortest distance from i to every vertex ('∞' = unreachable). Three nested loops: O(n³) time, O(n²) space. |
Intermediate-vertex recurrence
Let d_k[i][j] be the cheapest i → j path that uses only vertices 0 .. k - 1 as intermediate stops. Such a path either avoids vertex k or passes through it once, so d_{k+1}[i][j] = min(d_k[i][j], d_k[i][k] + d_k[k][j]). The base is d_0 = the direct edge weights. The program keeps just one matrix because round k never changes row k or column k (when there is no negative cycle), so the update is safe in place.
The recurrence is why the loop order matters: k is the outer loop, because each round must finish a whole matrix before the next vertex is allowed. The code keeps every layer so the idea is visible.
1static long[][][] floydLayers(int n, int[][] edges) { // layers[k][i][j]: cheapest i -> j using only intermediate vertices 0..k-12long INF = Long.MAX_VALUE / 4;3long[][][] layers = new long[n + 1][n][n];4for (long[][] m : layers) for (long[] r : m) Arrays.fill(r, INF);5for (int i = 0; i < n; i++) layers[0][i][i] = 0;6for (int[] e : edges) layers[0][e[0]][e[1]] = Math.min(layers[0][e[0]][e[1]], e[2]);7for (int k = 0; k < n; k++)8for (int i = 0; i < n; i++)9for (int j = 0; j < n; j++) {10long keep = layers[k][i][j];11long through = layers[k][i][k] < INF && layers[k][k][j] < INF ? layers[k][i][k] + layers[k][k][j] : INF;12layers[k + 1][i][j] = Math.min(keep, through); // avoid vertex k, or pass through it once13}14return layers;15}
Floyd-Warshall: all-pairs shortest paths and transitive closure
Interactive · edit the inputgrid · 5 × 5
d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop.
1long[][] floydWarshall(int n, int[][] edges) { // edges: {u, v, w}; null when a negative cycle exists2long INF = Long.MAX_VALUE / 4;3long[][] d = new long[n][n];4for (long[] r : d) Arrays.fill(r, INF);5for (int i = 0; i < n; i++) d[i][i] = 0;6for (int[] e : edges) d[e[0]][e[1]] = Math.min(d[e[0]][e[1]], e[2]); // keep the lightest parallel edge7for (int k = 0; k < n; k++) // intermediate vertices allowed so far: 0..k8for (int i = 0; i < n; i++)9for (int j = 0; j < n; j++)10if (d[i][k] < INF && d[k][j] < INF && d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];11for (int i = 0; i < n; i++) if (d[i][i] < 0) return null; // a vertex that returns to itself with negative cost12return d;13}
Live state
n
4
Execution trace (dry run table)Show 11 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | n=4 | d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop. |
| 2 | L7 | k=0 | Round 0: going through 0 changes nothing. |
| 3 | L9 | k=1 i=0 j=2 d[i][j]=1 | Through 1: d[0][1] + d[1][2] = 3 + -2 = 1, better than the old d[0][2]. Update it. |
| 4 | L9 | k=1 i=3 j=2 d[i][j]=-1 | Through 1: d[3][1] + d[1][2] = 1 + -2 = -1, better than the old d[3][2]. Update it. |
| 5 | L7 | k=1 | Round 1 done: some distances improved by going through 1. Intermediate vertices 0..1 are now allowed. |
| 6 | L9 | k=2 i=0 j=3 d[i][j]=3 | Through 2: d[0][2] + d[2][3] = 1 + 2 = 3, better than the old d[0][3]. Update it. |
| 7 | L9 | k=2 i=1 j=3 d[i][j]=0 | Through 2: d[1][2] + d[2][3] = -2 + 2 = 0, better than the old d[1][3]. Update it. |
| 8 | L7 | k=2 | Round 2 done: some distances improved by going through 2. Intermediate vertices 0..2 are now allowed. |
| 9 | L9 | k=3 i=2 j=1 d[i][j]=3 | Through 3: d[2][3] + d[3][1] = 2 + 1 = 3, better than the old d[2][1]. Update it. |
| 10 | L7 | k=3 | Round 3 done: some distances improved by going through 3. Intermediate vertices 0..3 are now allowed. |
| 11 | L12 | return=[0, 3, 1, 3] | [-, 0, -2, 0] | [-, 3, 0, 2] | [-, 1, -1, 0] improvements=5 | The diagonal is all zeros, so there is no negative cycle. Row i holds the shortest distance from i to every vertex ('∞' = unreachable). Three nested loops: O(n³) time, O(n²) space. |
Negative cycles
If d[i][i] becomes negative, vertex i can leave and come back with negative total cost: it lies on a negative closed walk. After the loops, every vertex on a negative cycle has a negative diagonal entry, and a negative entry always means such a closed walk exists. A pair (i, j) whose paths can pass through a negative cycle has no finite shortest distance.
With a negative cycle, numbers can shrink exponentially during the loops, so a robust implementation clamps values at a large negative bound to avoid overflow. The *Negative cycle shows on the diagonal* preset reports d[0][0] = -1.
1static boolean[] onNegativeCycle(int n, int[][] edges) { // vertices on a negative closed walk2long INF = Long.MAX_VALUE / 4;3long[][] d = new long[n][n];4for (long[] r : d) Arrays.fill(r, INF);5for (int i = 0; i < n; i++) d[i][i] = 0;6for (int[] e : edges) d[e[0]][e[1]] = Math.min(d[e[0]][e[1]], e[2]);7for (int k = 0; k < n; k++)8for (int i = 0; i < n; i++)9for (int j = 0; j < n; j++)10if (d[i][k] < INF && d[k][j] < INF) d[i][j] = Math.max(-INF, Math.min(d[i][j], d[i][k] + d[k][j])); // clamp: no overflow11boolean[] bad = new boolean[n];12for (int i = 0; i < n; i++) bad[i] = d[i][i] < 0;13return bad;14}
Floyd-Warshall: all-pairs shortest paths and transitive closure
Interactive · edit the inputgrid · 5 × 5
d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop.
1long[][] floydWarshall(int n, int[][] edges) { // edges: {u, v, w}; null when a negative cycle exists2long INF = Long.MAX_VALUE / 4;3long[][] d = new long[n][n];4for (long[] r : d) Arrays.fill(r, INF);5for (int i = 0; i < n; i++) d[i][i] = 0;6for (int[] e : edges) d[e[0]][e[1]] = Math.min(d[e[0]][e[1]], e[2]); // keep the lightest parallel edge7for (int k = 0; k < n; k++) // intermediate vertices allowed so far: 0..k8for (int i = 0; i < n; i++)9for (int j = 0; j < n; j++)10if (d[i][k] < INF && d[k][j] < INF && d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];11for (int i = 0; i < n; i++) if (d[i][i] < 0) return null; // a vertex that returns to itself with negative cost12return d;13}
Live state
n
4
Execution trace (dry run table)Show 11 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | n=4 | d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop. |
| 2 | L7 | k=0 | Round 0: going through 0 changes nothing. |
| 3 | L9 | k=1 i=0 j=2 d[i][j]=1 | Through 1: d[0][1] + d[1][2] = 3 + -2 = 1, better than the old d[0][2]. Update it. |
| 4 | L9 | k=1 i=3 j=2 d[i][j]=-1 | Through 1: d[3][1] + d[1][2] = 1 + -2 = -1, better than the old d[3][2]. Update it. |
| 5 | L7 | k=1 | Round 1 done: some distances improved by going through 1. Intermediate vertices 0..1 are now allowed. |
| 6 | L9 | k=2 i=0 j=3 d[i][j]=3 | Through 2: d[0][2] + d[2][3] = 1 + 2 = 3, better than the old d[0][3]. Update it. |
| 7 | L9 | k=2 i=1 j=3 d[i][j]=0 | Through 2: d[1][2] + d[2][3] = -2 + 2 = 0, better than the old d[1][3]. Update it. |
| 8 | L7 | k=2 | Round 2 done: some distances improved by going through 2. Intermediate vertices 0..2 are now allowed. |
| 9 | L9 | k=3 i=2 j=1 d[i][j]=3 | Through 3: d[2][3] + d[3][1] = 2 + 1 = 3, better than the old d[2][1]. Update it. |
| 10 | L7 | k=3 | Round 3 done: some distances improved by going through 3. Intermediate vertices 0..3 are now allowed. |
| 11 | L12 | return=[0, 3, 1, 3] | [-, 0, -2, 0] | [-, 3, 0, 2] | [-, 1, -1, 0] improvements=5 | The diagonal is all zeros, so there is no negative cycle. Row i holds the shortest distance from i to every vertex ('∞' = unreachable). Three nested loops: O(n³) time, O(n²) space. |
Transitive closure
Replace distances with booleans and +/min with and/or: reach[i][j] |= reach[i][k] && reach[k][j]. This is **Warshall's algorithm**; Floyd's shortest-path version is the same loops with a different operation. After round k, reach[i][j] says whether a path exists through intermediate vertices 0..k.
With bitsets the inner j loop becomes one word-wise OR: reach[i].or(reach[k]) whenever i reaches k, giving O(n³ / 64). Switch the animation to *Transitive closure* to see 1 and 0 instead of distances.
1static BitSet[] closureBits(int n, int[][] edges) { // reach[i].get(j): there is a path from i to j (every vertex reaches itself)2BitSet[] reach = new BitSet[n];3for (int i = 0; i < n; i++) { reach[i] = new BitSet(n); reach[i].set(i); }4for (int[] e : edges) reach[e[0]].set(e[1]);5for (int k = 0; k < n; k++)6for (int i = 0; i < n; i++)7if (reach[i].get(k)) reach[i].or(reach[k]); // one OR replaces the loop over j8return reach;9}
Floyd-Warshall: all-pairs shortest paths and transitive closure
Interactive · edit the inputgrid · 5 × 5
d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop.
1long[][] floydWarshall(int n, int[][] edges) { // edges: {u, v, w}; null when a negative cycle exists2long INF = Long.MAX_VALUE / 4;3long[][] d = new long[n][n];4for (long[] r : d) Arrays.fill(r, INF);5for (int i = 0; i < n; i++) d[i][i] = 0;6for (int[] e : edges) d[e[0]][e[1]] = Math.min(d[e[0]][e[1]], e[2]); // keep the lightest parallel edge7for (int k = 0; k < n; k++) // intermediate vertices allowed so far: 0..k8for (int i = 0; i < n; i++)9for (int j = 0; j < n; j++)10if (d[i][k] < INF && d[k][j] < INF && d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];11for (int i = 0; i < n; i++) if (d[i][i] < 0) return null; // a vertex that returns to itself with negative cost12return d;13}
Live state
n
4
Execution trace (dry run table)Show 11 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | n=4 | d[i][j] is the cheapest known cost from i to j using no intermediate vertices: the arrow weight (lightest if parallel), 0 on the diagonal, ∞ otherwise. Each round k allows vertex k as an intermediate stop. |
| 2 | L7 | k=0 | Round 0: going through 0 changes nothing. |
| 3 | L9 | k=1 i=0 j=2 d[i][j]=1 | Through 1: d[0][1] + d[1][2] = 3 + -2 = 1, better than the old d[0][2]. Update it. |
| 4 | L9 | k=1 i=3 j=2 d[i][j]=-1 | Through 1: d[3][1] + d[1][2] = 1 + -2 = -1, better than the old d[3][2]. Update it. |
| 5 | L7 | k=1 | Round 1 done: some distances improved by going through 1. Intermediate vertices 0..1 are now allowed. |
| 6 | L9 | k=2 i=0 j=3 d[i][j]=3 | Through 2: d[0][2] + d[2][3] = 1 + 2 = 3, better than the old d[0][3]. Update it. |
| 7 | L9 | k=2 i=1 j=3 d[i][j]=0 | Through 2: d[1][2] + d[2][3] = -2 + 2 = 0, better than the old d[1][3]. Update it. |
| 8 | L7 | k=2 | Round 2 done: some distances improved by going through 2. Intermediate vertices 0..2 are now allowed. |
| 9 | L9 | k=3 i=2 j=1 d[i][j]=3 | Through 3: d[2][3] + d[3][1] = 2 + 1 = 3, better than the old d[2][1]. Update it. |
| 10 | L7 | k=3 | Round 3 done: some distances improved by going through 3. Intermediate vertices 0..3 are now allowed. |
| 11 | L12 | return=[0, 3, 1, 3] | [-, 0, -2, 0] | [-, 3, 0, 2] | [-, 1, -1, 0] improvements=5 | The diagonal is all zeros, so there is no negative cycle. Row i holds the shortest distance from i to every vertex ('∞' = unreachable). Three nested loops: O(n³) time, O(n²) space. |
Practice ladder
Now try it yourself
Easy
- Network Delay Time
Medium
- Find the City With the Smallest Number of Neighbors at a Threshold Distance
- Course Schedule IV
Advanced
- Shortest Path Visiting All Nodes
- Minimum Weighted Subgraph With the Required Paths
Key takeaways
- Floyd-Warshall: for each k, for each pair, try the route through k; O(n³) time, O(n²) space.
- The recurrence allows intermediate vertices 0..k; k must be the outer loop.
- A negative value on the diagonal marks a vertex on a negative cycle; clamp values to avoid overflow.
- The same loops with boolean operations give the transitive closure, fast with bitsets.