G27
Self-checkedTier 1 11 min readalgo.all-pairs-floyd

Floyd-Warshall

All-pairs shortest paths by dynamic programming: D[i][j] after iteration k is the shortest path using only the vertices 0 to k as intermediates; the three nested loops and why k must be the outer one, O(V^3) time and O(V^2) space, negative edges and negative cycles (a negative diagonal), path reconstruction with a predecessor matrix, transitive closure (Warshall), and the comparison with running Dijkstra or Bellman-Ford from every vertex.

Why it exists

Sometimes the distances between all pairs of vertices are needed: a table of distances between cities, the routing tables of a small network, the answer to "can ii reach jj?" for every pair. One could run Dijkstra or Bellman-Ford from every vertex; Floyd-Warshall instead does it with three nested loops and a matrix, in Θ(V3)\Theta(V^3) time. It is simple, handles negative edges, detects negative cycles, and is the standard example of a dynamic program over the set of allowed intermediate vertices.

Prerequisites

You need the dynamic programming recipe of state, recurrence and filling order (dynamic programming).

Precise definitions

All-pairs shortest paths. For a weighted directed graph on vertices 0,…,V−10, \ldots, V - 1, the matrix δ[i][j]\delta[i][j] of shortest-path distances from ii to jj for every ordered pair.

Weight matrix WW: W[i][i]=0W[i][i] = 0, W[i][j]W[i][j] the weight of the edge i→ji \to j, and ∞\infty if there is no such edge.

Intermediate vertex. A vertex of a path other than its two ends.

State. D(k)[i][j]D^{(k)}[i][j] is the weight of a shortest path from ii to jj all of whose intermediate vertices are in {0,1,…,k}\{0, 1, \ldots, k\} (D(−1)=WD^{(-1)} = W: no intermediate vertex allowed, direct edges only).

How it works

The recurrence

A shortest path from ii to jj with intermediate vertices in {0,…,k}\{0, \ldots, k\} either does not use vertex kk (then it is a shortest path with intermediates in {0,…,k−1}\{0, \ldots, k-1\}) or uses it once (a shortest path is simple): it consists of a shortest path from ii to kk and a shortest path from kk to jj, both with intermediates in {0,…,k−1}\{0, \ldots, k-1\}:

D(k)[i][j]=min⁡(D(k−1)[i][j], D(k−1)[i][k]+D(k−1)[k][j]).D^{(k)}[i][j] = \min\bigl(D^{(k-1)}[i][j],\ D^{(k-1)}[i][k] + D^{(k-1)}[k][j]\bigr).

The answer is D(V−1)D^{(V-1)}. The code needs only one matrix, updated in place:

for (k = 0; k < V; k++)
    for (i = 0; i < V; i++)
        for (j = 0; j < V; j++)
            if (d[i][k] + d[k][j] < d[i][j])
                d[i][j] = d[i][k] + d[k][j];

In-place update is safe: during iteration kk the row kk and the column kk do not change (without negative cycles, D[k][k]=0D[k][k] = 0, so D[i][k]+D[k][k]=D[i][k]D[i][k] + D[k][k] = D[i][k]), so the values read are those of D(k−1)D^{(k-1)}. Time Θ(V3)\Theta(V^3) (exactly V3V^3 executions of the innermost test), space Θ(V2)\Theta(V^2). The loop over kk must be the outermost: a loop order with kk inside gives wrong answers.

The page graph, step by step

The graph has 55 vertices and the edges 0→1 (3)0 \to 1\,(3), 0→2 (5)0 \to 2\,(5), 1→2 (−2)1 \to 2\,(-2), 1→3 (4)1 \to 3\,(4), 2→3 (2)2 \to 3\,(2), 3→0 (6)3 \to 0\,(6), 3→4 (1)3 \to 4\,(1), 4→1 (2)4 \to 1\,(2). The program prints the matrix before the loop and after each value of kk; ∞\infty is written inf.

#include <stdio.h>
 
#define N 5
#define INF 1000000
 
static void show(int d[N][N]) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (d[i][j] >= INF) printf(" inf");
            else printf(" %3d", d[i][j]);
        }
        printf("\n");
    }
}
 
int main(void) {
    int d[N][N];
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++) d[i][j] = i == j ? 0 : INF;
    d[0][1] = 3; d[0][2] = 5; d[1][2] = -2; d[1][3] = 4; d[2][3] = 2; d[3][0] = 6; d[3][4] = 1; d[4][1] = 2;
    printf("D0 (only direct edges):\n");
    show(d);
    for (int k = 0; k < N; k++) {                        /* allow vertex k as an intermediate vertex */
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (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];
                }
            }
        }
        printf("after k = %d:\n", k);
        show(d);
    }
    int negative = 0;
    for (int i = 0; i < N; i++) if (d[i][i] < 0) negative = 1;
    printf("negative cycle: %s\n", negative ? "yes" : "no");
    return 0;
}
D0 (only direct edges):
   0   3   5 inf inf
 inf   0  -2   4 inf
 inf inf   0   2 inf
   6 inf inf   0   1
 inf   2 inf inf   0
after k = 0:
   0   3   5 inf inf
 inf   0  -2   4 inf
 inf inf   0   2 inf
   6   9  11   0   1
 inf   2 inf inf   0
after k = 1:
   0   3   1   7 inf
 inf   0  -2   4 inf
 inf inf   0   2 inf
   6   9   7   0   1
 inf   2   0   6   0
after k = 2:
   0   3   1   3 inf
 inf   0  -2   0 inf
 inf inf   0   2 inf
   6   9   7   0   1
 inf   2   0   2   0
after k = 3:
   0   3   1   3   4
   6   0  -2   0   1
   8  11   0   2   3
   6   9   7   0   1
   8   2   0   2   0
after k = 4:
   0   3   1   3   4
   6   0  -2   0   1
   8   5   0   2   3
   6   3   1   0   1
   8   2   0   2   0
negative cycle: no

The animation below shows the same run one improvement at a time: first the matrix D(−1)D^{(-1)} of direct edges, then for each kk a step that says which vertex is now allowed and one step for every entry that improves, with the path that gives the improvement.

Floyd-Warshall on a 5-vertex graph, one improvement per step: start

Computed by a script, not typed
  1. 1D[i][j] = weight of the edge i -> j (0 if i = j, infinity if none)
  2. 2D[i][j] = min(D[i][j], D[i][k] + D[k][j]) for k = 0, 1, ...
from \ to01234
0035∞∞
1∞0-24∞
2∞∞02∞
36∞∞01
4∞2∞∞0

D0

Step 0

D0: the distances using only direct edges (infinity if there is no edge). Rows are the start vertex i, columns the end vertex j. Vertex k = 0, 1, ... will be allowed one at a time as an intermediate vertex.

0 / 25
computed by script: the quantities are measured by running C, or evaluated, one row at a time. python3 scripts/gen-algo-paths-traces.py

For example, with k=1k = 1 the entry D[0][2]D[0][2] improves from 55 to 11: the path 0→1→20 \to 1 \to 2 costs 3+(−2)3 + (-2), cheaper than the direct edge. With k=3k = 3 many entries become finite for the first time: vertex 33 links the vertices that could reach it with those it can reach. The final matrix has no negative diagonal entry, so the graph has no negative cycle.

Negative edges and negative cycles

Floyd-Warshall works with negative edges as long as there is no negative cycle. If there is one, some diagonal entry D[i][i]D[i][i] becomes negative (a path from ii back to ii of negative weight): check the diagonal after the loops. (Example: edges 0→1 (1)0 \to 1\,(1), 1→2 (−3)1 \to 2\,(-3), 2→0 (1)2 \to 0\,(1) give D[0][0]=−1D[0][0] = -1.) In that case the other entries may be meaningless.

Recovering the paths

Keep a second matrix next[i][j]\text{next}[i][j] (the first vertex after ii on a shortest path to jj), initialised to jj for every edge i→ji \to j; whenever D[i][j]D[i][j] improves through kk set next[i][j]=next[i][k]\text{next}[i][j] = \text{next}[i][k]. A path is then read by following next\text{next} from ii until jj. (Equivalently a predecessor matrix.)

Transitive closure (Warshall's algorithm)

To decide only whether jj is reachable from ii, use a Boolean matrix RR with R[i][i]=1R[i][i] = 1 and R[i][j]=1R[i][j] = 1 for each edge, and the same three loops with the update R[i][j]=R[i][j]∨(R[i][k]∧R[k][j])R[i][j] = R[i][j] \lor (R[i][k] \land R[k][j]). After iteration kk, R[i][j]=1R[i][j] = 1 exactly if there is a path from ii to jj with intermediate vertices in {0,…,k}\{0, \ldots, k\}. The result is the transitive closure of the edge relation, in Θ(V3)\Theta(V^3) time (checked against searches from every vertex). Searching from every vertex does the same job in Θ(V(V+E))\Theta(V(V + E)), which is faster for sparse graphs.

Which algorithm for all pairs?

MethodTimeNegative edges
Floyd-WarshallΘ(V3)\Theta(V^3)yes (no negative cycle)
Dijkstra from every vertex (binary heap)Θ(V(V+E)log⁡V)\Theta(V(V + E) \log V)no
Bellman-Ford from every vertexΘ(V2E)\Theta(V^2 E)yes
Johnson's algorithm (Bellman-Ford once to re-weight, then Dijkstra from every vertex)Θ(V(V+E)log⁡V)\Theta(V(V + E) \log V)yes

For dense graphs (E≈V2E \approx V^2) Floyd-Warshall's Θ(V3)\Theta(V^3) is as good as any and much simpler; for sparse graphs VV runs of Dijkstra (or Johnson's algorithm if there are negative edges) are faster.

Worked examples

EX 1A three-vertex run by hand

Easy

Edges 0→1 (2)0 \to 1\,(2), 1→2 (3)1 \to 2\,(3), 0→2 (7)0 \to 2\,(7). D(−1)D^{(-1)}: D[0][1]=2D[0][1] = 2, D[1][2]=3D[1][2] = 3, D[0][2]=7D[0][2] = 7, others ∞\infty (diagonal 00). k=0k = 0: nothing enters vertex 00 (D[i][0]=∞D[i][0] = \infty for i≠0i \ne 0), no change. k=1k = 1: D[0][2]D[0][2] is compared with D[0][1]+D[1][2]=2+3=5<7D[0][1] + D[1][2] = 2 + 3 = 5 < 7: improves to 55. k=2k = 2: no vertex leaves vertex 22: no change. Result: D[0][1]=2D[0][1] = 2, D[1][2]=3D[1][2] = 3, D[0][2]=5D[0][2] = 5.

EX 2How many operations?

Easy

The innermost comparison runs V3V^3 times: 125125 for the page graph, 10910^9 for V=1000V = 1000 (about a second or a few). Space: V2V^2 numbers. The number of improvements is far smaller (the animation shows them one by one).

EX 3What does D after iteration k mean?

Medium

In the page run after k=1k = 1 the entry D[4][2]=0D[4][2] = 0: the path 4→1→24 \to 1 \to 2 costs 2+(−2)=02 + (-2) = 0, using only vertex 11 as an intermediate, which is allowed. The entry D[2][0]D[2][0] is still ∞\infty after k=1k = 1: the only way from 22 to 00 goes through vertex 33 (2→3→02 \to 3 \to 0), which is not yet allowed; after k=3k = 3 it becomes 2+6=82 + 6 = 8.

EX 4Transitive closure

Medium

Edges 0→10 \to 1, 1→21 \to 2, 3→03 \to 0. Warshall: k=0k = 0: 3→0→13 \to 0 \to 1 gives R[3][1]=1R[3][1] = 1. k=1k = 1: 0→1→20 \to 1 \to 2 gives R[0][2]=1R[0][2] = 1 and 3→1→23 \to 1 \to 2 gives R[3][2]=1R[3][2] = 1. k=2k = 2, k=3k = 3: no new pairs. Closure: 00 reaches 1,21, 2; 11 reaches 22; 33 reaches 0,1,20, 1, 2; vertex 22 reaches nobody.

EX 5Typical statements

GATE level

(a) Time of Floyd-Warshall: Θ(V3)\Theta(V^3); space Θ(V2)\Theta(V^2). (b) Does it work with negative edges? Yes, unless there is a negative cycle. (c) How is a negative cycle detected? A negative entry on the diagonal. (d) What would go wrong if the loop over kk were innermost? The recurrence needs all paths with intermediates in {0..k−1}\{0..k-1\} finished before kk is added; with kk innermost they are not, and some shortest paths are missed. (e) For VV vertices running Dijkstra from each vertex with an array costs V⋅Θ(V2)=Θ(V3)V \cdot \Theta(V^2) = \Theta(V^3), the same as Floyd-Warshall, but without negative edges. (f) Warshall's algorithm computes: the transitive closure.

Cheat box

Cheat box

  • D(k)[i][j]=min⁡(D(k−1)[i][j], D(k−1)[i][k]+D(k−1)[k][j])D^{(k)}[i][j] = \min(D^{(k-1)}[i][j],\ D^{(k-1)}[i][k] + D^{(k-1)}[k][j]): shortest path with intermediates in {0..k}\{0..k\}. Start: direct edges, diagonal 00, else ∞\infty.
  • Loops: kk outermost, then ii, then jj. In place. Θ(V3)\Theta(V^3) time, Θ(V2)\Theta(V^2) space.
  • Negative edges fine; a negative diagonal entry means a negative cycle.
  • Paths: keep a next/predecessor matrix.
  • Warshall: same loops with OR/AND ⇒\Rightarrow transitive closure.
  • All-pairs alternatives: Dijkstra VV times (no negative edges), Johnson (negative edges, sparse).

Traps and GATE patterns

Connections

Floyd-Warshall is a dynamic program over the allowed intermediate vertices. It computes for all pairs what Dijkstra and Bellman-Ford compute for one source, and its Boolean form computes the reachability that depth-first search and strongly connected components find by searching.

PYQs

2 previous year questions from your book. I re-solved each one myself (1 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs

GATE 2005 · MCQ · 1 markQ39 · page 103 of the book
The Asymptotic Notation of computing the
transitive closure of a binary relation on a set of n
elements is known to be:
  • (a)O(n)
  • (b)O(nlogn)
  • (c)O(n3/2)
  • (d)O(n3)
GATE 2016 · Set 1 · NAT · 2 marksQ24 · page 116 of the book
Consider the weighted undirected graph with 4
vertices, where the weight of edge {i, j} is given by
the entry W ij in the matrix W.

Self-check

Question 1

What is the time and space of Floyd-Warshall for a graph with VV vertices?

Show answer

Θ(V3)\Theta(V^3) time and Θ(V2)\Theta(V^2) space.

Question 2

What does the entry D(k)[i][j]D^{(k)}[i][j] represent?

Show answer

The weight of a shortest path from ii to jj whose intermediate vertices all belong to {0,1,…,k}\{0, 1, \ldots, k\}.

Question 3

How can you tell from the final matrix that the graph has a negative cycle?

Show answer

Some diagonal entry D[i][i]D[i][i] is negative.

Question 4

How many times does the innermost statement of the three loops execute for V=10V = 10?

Show answer

103=100010^3 = 1000.

Revision card

One-screen revision

  • D(k)[i][j]=min⁡(D(k−1)[i][j],D(k−1)[i][k]+D(k−1)[k][j])D^{(k)}[i][j] = \min(D^{(k-1)}[i][j], D^{(k-1)}[i][k] + D^{(k-1)}[k][j]); kk outermost; in place.
  • Θ(V3)\Theta(V^3) time, Θ(V2)\Theta(V^2) space; negative edges OK; negative diagonal ⇒\Rightarrow negative cycle.
  • Warshall: OR/AND, transitive closure. Paths via a next matrix.
  • Sparse graphs: VV times Dijkstra, or Johnson.

References

  • ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Chapter 25 All-Pairs Shortest Paths (25.2 the Floyd-Warshall algorithm, 25.2 transitive closure)
  • ·Sedgewick and Wayne, Algorithms (4th edition), Section 4.4 and Section 4.2 (Warshall's algorithm for transitive closure)
  • ·Dasgupta, Papadimitriou, Vazirani, Algorithms, Section 6.6 All-pairs shortest paths

Verification

On 300 random directed graphs of up to 6 vertices every row of the Floyd-Warshall matrix was compared with a brute force over ALL simple paths, and for every k the matrix after iteration k was compared with the shortest path whose intermediate vertices all lie in {0, ..., k}. Negative cycles produced a negative diagonal entry; the transitive closure by the same three loops with OR and AND equalled reachability found by search. The matrix of the page graph was also compared with Bellman-Ford from each of its five vertices. The table is a real run of the page code (one step per improved entry, the single assignment instrumented) and its final matrix equals the printed one. Textbook chapter references are from memory. Johnson's algorithm is mentioned from the literature and not tested. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.

last verified: never

My notes (saved in this browser)