Floyd-Warshall Algorithm
Imagine a logistics company needs the cheapest route between every pair of warehouses. Its network has a few hundred cities. Roads may have tolls, one-way restrictions, or even negative price adjustments from promotional routes. Running a single-source shortest-path algorithm from every warehouse would work. But it invites a simpler question. Is there one clean, uniform procedure that computes every pairwise distance at once? It should do so without special-casing negative edges or repeatedly rebuilding priority queues. The Floyd-Warshall algorithm answers this with an elegant dynamic program. It fills in an entire distance matrix in a fixed number of passes. It uses nothing more exotic than three nested loops. This matters far beyond toy examples. Compilers use all-pairs reachability and shortest-path style analyses for optimization. Network routing protocols precompute pairwise costs between routers. Database query optimizers reason about join-cost matrices. Computational biology tools use transitive-closure-like computations on interaction graphs. Whenever a system needs to know "what is the best (or whether any) connection exists between every pair of entities," a Floyd-Warshall-style computation is a natural fit. This is especially true when the graph is dense enough. In that case, computing single-source shortest paths V times would not be dramatically cheaper. The big picture is that Floyd-Warshall reframes the All-Pairs Shortest Paths Overview problem. It becomes a sequence of relaxed questions. Each asks: "what is the shortest path from i to j that is only allowed to pass through intermediate vertices from the set {1, ..., k}?" As k grows from 0 to V, the algorithm allows itself to route through one more vertex at each stage. A beautifully simple recurrence updates every pair's distance in O(1) work per pair per stage. The result after V stages is the true shortest-path matrix over the whole vertex set. This assumes no negative-weight cycle is reachable, which the algorithm can also detect. This article gives the precise dynamic-programming formulation. It includes a full pseudocode implementation with path reconstruction. It also includes a diagram of the k-th stage's relaxation logic. It also provides a rigorous correctness proof by induction on k. There is a worked trace on a small directed graph with a negative edge. Finally, it discusses where this algorithm is used in real systems. It also covers where it should be avoided in favor of sparse-graph alternatives.
- Floyd-Warshall computes shortest-path distances between all pairs of vertices in a weighted directed graph in one unified dynamic program.
- The core recurrence restricts paths to intermediate vertices from {1,...,k} and grows k from 0 to V, one vertex at a time.
- Time complexity is Θ(V³) and space is Θ(V²), independent of the number of edges — ideal for dense graphs.
- It correctly handles negative edge weights, as long as there is no negative-weight cycle reachable between the pair in question.
- Negative cycles are detected by checking whether any diagonal entry d[i][i] becomes negative after the full computation.
- A predecessor matrix, updated alongside distances, allows reconstruction of actual shortest paths, not just their lengths.
Floyd-Warshall finds all-pairs shortest paths by asking, for a growing set of allowed intermediate vertices, whether routing through the newest vertex ever improves a pair's distance.
The idea
Let be a directed graph with vertices, labeled through , and a weight function on edges that may include negative weights but no negative-weight cycle overall (the algorithm can detect cycles that violate this). The goal is the all-pairs shortest path problem: compute, for every ordered pair , the weight of a shortest path from to , denoted . The key idea is to parametrize paths by which vertices they are permitted to pass through internally. Define as the weight of a shortest path from to using only vertices from as intermediate (interior) vertices — the endpoints and themselves are always allowed regardless of whether they lie in . When , no intermediate vertices are allowed at all, so is just the direct edge weight if the edge exists, if , and otherwise. When , every vertex is an allowed intermediate, so , the true shortest-path distance. The recurrence connecting consecutive stages is the heart of the method. A shortest path from to restricted to intermediates in either avoids vertex entirely — in which case it is already optimal using only — or it passes through exactly once (a shortest path never visits the same vertex twice, assuming no negative cycles), splitting into a sub-path from to and a sub-path from to , both restricted to intermediates in :
This is a textbook dynamic-programming recurrence: overlapping subproblems (many pairs share the same values) and optimal substructure (a shortest path's sub-paths are themselves shortest). Because depends only on , the algorithm can reuse a single matrix in place, updating it k by k, which is what gives the method its compact, loop-based implementation.
How it works
The algorithm maintains an distance matrix , initialized from the direct edge weights, and optionally a predecessor matrix for path reconstruction. It then performs rounds, one per candidate intermediate vertex , and in each round relaxes every pair using as a potential waypoint. Because is updated in place, the pseudocode never needs to keep the old and new matrices separate — this is legal precisely because and are provably equal to and respectively (routing through itself as an intermediate to reach cannot help, since that would require a negative-weight cycle through ), so overwriting row and column during round does not corrupt the values the recurrence needs. ``` FLOYD-WARSHALL(W, n): // W is an n x n matrix: W[i][j] = weight of edge i->j, // 0 if i = j, and +infinity if no edge exists. D = copy of W Pi = n x n matrix, Pi[i][j] = i if W[i][j] < infinity and i != j, else NIL for k = 1 to n: for i = 1 to n: for j = 1 to n: if D[i][k] + D[k][j] < D[i][j]: D[i][j] = D[i][k] + D[k][j] Pi[i][j] = Pi[k][j] // Negative-cycle check for i = 1 to n: if D[i][i] < 0: report "negative-weight cycle reachable from and to vertex i" return D, Pi ``` ``` RECONSTRUCT-PATH(Pi, i, j): if i == j: return [i] if Pi[i][j] == NIL: return "no path" path = RECONSTRUCT-PATH(Pi, i, Pi[i][j]) append j to path return path ``` A few implementation notes worth internalizing:
- The loop order must be outermost, then , then (or , swapped — order between them doesn't matter, but must be outermost). This is what guarantees that when computing , the matrix already holds values for all the entries the recurrence reads.
- The predecessor update `Pi[i][j] = Pi[k][j]` records that the shortest known path now arrives at the same way the shortest path does — because the new path is "old path" followed by "old path," and the predecessor of on that concatenation is whatever preceded on the leg.
- Detecting negative cycles by scanning the diagonal works because starts at and can only become negative if some cycle through (via allowed intermediates) has negative total weight.
Complexity & correctness
Time complexity. The algorithm executes three nested loops, each running exactly times, and the body of the innermost loop does work (one comparison, possibly one assignment). This gives
with no dependence on the number of edges . This is a deliberate and important design point: for dense graphs, where , running Dijkstra's algorithm from every vertex costs with a Fibonacci heap — asymptotically comparable — but Floyd-Warshall achieves this with far smaller constants and no data structure overhead (no heaps, no priority queue bookkeeping), and it works unmodified with negative edge weights, which Dijkstra's algorithm cannot handle directly. Running the Bellman-Ford algorithm from every source costs , which is asymptotically worse than whenever , making Floyd-Warshall preferable on dense graphs even though both handle negative weights.
Space complexity. The algorithm needs space for the distance matrix and, if paths must be reconstructed, another for the predecessor matrix. This is optimal in the sense that the output itself — the full pairwise distance matrix — already requires space to store.
Correctness proof sketch. The proof is by induction on , formalizing the recurrence from the idea section. Claim: after the round for a given completes, for all pairs , where is the true shortest-path distance using only intermediates from . Base case (): before any round runs, holds the initial matrix , which is exactly : the direct edge weight or or , since no intermediate vertices are allowed. Inductive step: assume holds values entering round . Consider any pair and let be a shortest path from to restricted to intermediates in . Two cases: 1. does not use vertex as an interior vertex. Then is a path with intermediates only in , so its weight is at least , and since is itself achievable with intermediates in , we get in this case. 2. does use vertex as an interior vertex. Because shortest paths (under the no-reachable-negative-cycle assumption) never repeat a vertex, appears exactly once, splitting into and , each using only intermediates from (they cannot reuse ). By optimal substructure, and must themselves be shortest paths for their respective restricted problems — otherwise could be shortened — so weight. Taking the minimum over both cases gives exactly the recurrence , which is precisely the update the pseudocode performs, reading old (-stage) values because rows/columns have not yet been altered by this round in a way that invalidates them (row and column are unchanged during round , since can't improve via itself without a negative cycle, and any update to via is a no-op unless ). This closes the induction, so after round , for all pairs.
Negative cycles. If a negative-weight cycle is reachable from and reaches back to , then is not well-defined (a walk around the cycle repeatedly decreases cost without bound), and the algorithm will compute for some once reaches a vertex on that cycle — this is exactly why the diagonal check at the end is a valid and complete negative-cycle detector. If no negative cycle exists anywhere in the graph, all shortest paths are simple (no repeated vertices), which is precisely the assumption the case-2 argument above relies on.
Tradeoffs. The time is a hard floor for this method regardless of how sparse the graph is — Floyd-Warshall does not exploit sparsity at all, unlike Johnson's algorithm, which reweights edges and runs Dijkstra from each vertex in , a better bound when . Floyd-Warshall's advantages are its extreme simplicity, small constant factors, in-place matrix updates, and uniform handling of negative edges without needing a separate reweighting step.
Worked example
Consider a small directed graph with vertices and edges (with weights, some negative):
The initial distance matrix (rows = from, columns = to, shown as `∞`):
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | ∞ | −2 | ∞ |
| 2 | 4 | 0 | 3 | ∞ |
| 3 | ∞ | ∞ | 0 | 2 |
| 4 | ∞ | −1 | ∞ | 0 |
Round (allow routing through vertex 1). Check every for improvement via . Only row 2 has a finite entry to column 1 (), so candidates are via : gives current — improves! Update . No other pair improves since only vertex 2 can reach vertex 1.
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | ∞ | −2 | ∞ |
| 2 | 4 | 0 | 2 | ∞ |
| 3 | ∞ | ∞ | 0 | 2 |
| 4 | ∞ | −1 | ∞ | 0 |
Round (allow routing through vertex 2). Rows with finite entry to column 2: row 4 (). Candidates via : : , improve . : , improve . ( is self, skip.)
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | ∞ | −2 | ∞ |
| 2 | 4 | 0 | 2 | ∞ |
| 3 | ∞ | ∞ | 0 | 2 |
| 4 | 3 | −1 | 1 | 0 |
Round (allow routing through vertex 3). Rows with finite entry to column 3: row 1 (), row 2 (), row 4 (). For each, add and compare to ; only is finite for .
- Row 1: → update .
- Row 2: → update .
- Row 4: , current , no improvement.
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | ∞ | −2 | 0 |
| 2 | 4 | 0 | 2 | 4 |
| 3 | ∞ | ∞ | 0 | 2 |
| 4 | 3 | −1 | 1 | 0 |
Round (allow routing through vertex 4). Column 4 finite entries: rows 1 (), 2 (), 3 (). Row 4 itself gives .
- Row 1 via 4: : : → update . : , no change to diagonal needed (would be , stays 0, correctly no negative cycle). : , current , no improvement.
- Row 2 via 4: : : , no. : would be self, skip. : , no.
- Row 3 via 4: : : → update . : → update .
Final matrix :
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | −1 | −2 | 0 |
| 2 | 4 | 0 | 2 | 4 |
| 3 | 5 | 1 | 0 | 2 |
| 4 | 3 | −1 | 1 | 0 |
All diagonal entries remain , so there is no negative-weight cycle. Sanity-check one entry by hand: corresponds to the path with weight , matching the direct-edge check ( has no direct edge) — confirming the dynamic program found a genuinely non-obvious multi-hop shortest path.
Common pitfalls & traps
| Pitfall | Why it breaks the algorithm |
|---|---|
| Putting the loop order as `i, j, k` instead of `k` outermost | Breaks the DP invariant — updates can read values from the current stage instead of stage , silently producing wrong (sometimes still "correct-looking") distances on some graphs |
| Forgetting to initialize | Self-distances default to , corrupting every relaxation that legitimately routes through a return to the same vertex, and breaking negative-cycle detection |
| Using a sentinel like `999999` for instead of true infinity | Overflow: `D[i][k] + D[k][j]` can silently wrap or exceed valid ranges and falsely appear smaller than the real , injecting bogus "shortest paths" through non-edges |
| Assuming negative edges are always fine | Correct only if no negative cycle is reachable between the relevant pair; the algorithm still runs to completion on a negative-cycle graph but the output distances for affected pairs are meaningless |
| Reconstructing paths with a naive predecessor scheme that stores instead of | Produces an incorrect predecessor chain that doesn't actually trace the shortest path recorded by |
| Running Floyd-Warshall on a very sparse huge graph (large , small ) | Wastes time and memory when Johnson's algorithm or repeated Dijkstra would be dramatically faster |
Pioneers & history
The algorithm's dynamic-programming core — reasoning about paths restricted to a growing set of allowed intermediate vertices — was published by Robert Floyd in 1962 as a concise procedure for shortest paths, building on a closely related transitive-closure algorithm published by Stephen Warshall in the same year. Bernard Roy had independently described an essentially equivalent algorithm in 1959 for the related problem of computing transitive closure and shortest paths, which is why some treatments call it the Roy-Floyd-Warshall or Roy-Warshall algorithm. The recurrence itself echoes ideas from dynamic programming as formalized by Richard Bellman in the 1950s, and the entire family of algorithms sits within the broader tradition of the Bellman Equation style of reasoning about optimal substructure.
Origins · 1 minRobert W. Floyd and how this was discovered
Robert Floyd was a computer scientist whose contributions shaped much of theoretical computer science's vocabulary: he pioneered the use of assertions and invariants to prove program correctness (Floyd-Hoare logic), contributed foundational parsing techniques, and — most relevant here — published his all-pairs shortest-path algorithm as a short, elegant note showing how a triple nested loop over intermediate vertices suffices to solve a problem that appears to demand far more machinery. He won the Turing Award in 1978 for influence on programming language design and algorithm methodology, cementing his role as one of the field's most rigor-driven early theorists.
Where this matters
Network routing and traffic engineering systems use all-pairs shortest-path precomputation to answer "what is the best path between any two routers" queries instantly without recomputation, especially in software-defined networking controllers managing moderately sized dense topologies. Compilers and static analyzers use Floyd-Warshall-style transitive-closure computations for reachability analysis, alias analysis, and certain forms of dataflow analysis, where the "graph" is a control-flow or points-to graph and the goal is Boolean or weighted reachability between all pairs of program points. Game AI and pathfinding systems on relatively small, dense maps (grid regions, building interiors) sometimes precompute all-pairs distances offline with Floyd-Warshall so that at runtime any two points can be connected with an O(1) table lookup rather than an online search — a classic space-for-time tradeoff. Database query optimizers use related matrix-closure computations to estimate cheapest join orderings across many tables. Computational biology tools apply the same recurrence to interaction networks and metabolic pathway graphs to compute pairwise "distances" (interaction costs) between all molecules or genes in a dense subnetwork. More broadly, the algorithm is a canonical teaching example for the Dynamic Programming paradigm and is frequently contrasted with single-source methods like Dijkstra's Algorithm and Bellman-Ford Algorithm to illustrate when a dense, uniform, matrix-based approach outperforms repeated single-source computations, and it serves as the conceptual ancestor of Kleene's algorithm for regular expressions from finite automata, which uses an isomorphic intermediate-state recurrence.
Ask the tutor: How would you modify Floyd-Warshall to count the number of distinct shortest paths between every pair, not just their lengths?
Can Floyd-Warshall handle negative edge weights?
Yes, and this is one of its main advantages over running Dijkstra's algorithm from every vertex. Dijkstra's greedy correctness proof assumes non-negative weights, so it can produce wrong answers with negative edges. Floyd-Warshall's recurrence only assumes optimal substructure of shortest paths, which holds as long as there is no negative-weight cycle reachable between the pair being computed. The algorithm will still run to completion even in the presence of a negative cycle; it just won't produce meaningful finite distances for pairs affected by that cycle, and it flags this by leaving a negative value on the diagonal.
Why is the time complexity Θ(V³) and not better, given that the graph might be sparse?
The three nested loops each run V times unconditionally — the algorithm never looks at the edge list beyond initialization, so it does the same Θ(V³) amount of work regardless of whether the graph has V² edges or just V edges. This is fundamentally different from Dijkstra or Bellman-Ford, whose running times explicitly depend on E. For sparse graphs, this makes Floyd-Warshall asymptotically wasteful compared to running a sparse-graph single-source algorithm from every vertex, or using Johnson's algorithm, which reweights edges to remove negatives and then runs Dijkstra V times in roughly O(V² log V + VE) time.
How is the actual shortest path reconstructed, not just its length?
A predecessor matrix Π is maintained alongside the distance matrix D. Initially Π[i][j] is set to i if there's a direct edge (the predecessor of j on the direct edge is i), or NIL otherwise. Whenever the algorithm updates D[i][j] because routing through k is better, it also sets Π[i][j] = Π[k][j], meaning "the predecessor of j on this new best i-to-j path is whatever precedes j on the best k-to-j path." Reconstructing the path from i to j is then a recursive walk backward through Π until reaching i, then reversing the collected vertices.
What happens if two different vertices k give the same shortest distance for a pair (i,j)?
Ties don't cause any correctness issue: the algorithm as written uses strict less-than (`<`) in the comparison, so it keeps the first shortest path found (through the smallest-indexed k that achieves the optimum) and simply never overwrites it with an equally long alternative. Both paths are valid shortest paths since they have equal weight — the algorithm just picks one deterministically. If all such optimal paths are needed (not just one), the matrix would need to store sets of predecessors rather than a single one, at extra space cost.
Is Floyd-Warshall a greedy algorithm or dynamic programming?
It is dynamic programming, not greedy. Greedy algorithms like Dijkstra's make an irrevocable locally optimal choice at each step and never revisit it. Floyd-Warshall instead builds up a table of subproblem solutions — d_k(i,j) for increasing k — where each stage explicitly reconsiders every pair of vertices and takes the minimum of the old value and a new candidate. The distinguishing DP signature is exactly this recurrence with a min over overlapping subproblems, combined with the fact that filling in a full table (rather than making single irreversible decisions) is what guarantees the final answer is globally optimal.
How does Floyd-Warshall relate to matrix multiplication and transitive closure?
There's a deep structural analogy: if you replace ordinary addition and minimum with Boolean OR and AND respectively, the exact same triple-loop structure computes the transitive closure of a directed graph (reachability between all pairs), which is the algorithm Stephen Warshall published. More generally, both are instances of computing powers of a matrix over a "min-plus" or "closed semiring" algebra, and repeated squaring of this semiring-matrix can compute the same all-pairs result in O(V³ log V) time using O(log V) matrix multiplications — asymptotically worse in practice than the direct Θ(V³) triple loop for this specific problem, but conceptually unifying shortest paths, reachability, and certain parsing algorithms under one algebraic framework.
Can Floyd-Warshall be parallelized or optimized further in practice?
Yes. The innermost double loop over (i, j) for a fixed k is fully data-parallel — each cell's update depends only on values from before round k started, plus row k and column k, so it maps naturally onto GPUs or SIMD hardware, and cache-blocked ("tiled") versions significantly improve real-world performance by improving locality of the O(V²) matrix accesses per round. Some implementations also exploit sparsity heuristically by skipping (i, j) pairs known to still be infinite, though in the worst case this doesn't change the asymptotic bound. The underlying Θ(V³) operation count, however, is a hard lower bound for this specific algorithmic approach; no known variant of Floyd-Warshall itself beats it, though semiring matrix-multiplication tricks can asymptotically beat V³ using fast matrix multiplication in specialized settings.