Articles

Floyd-Warshall: All-Pairs Shortest Paths

Floyd-Warshall finds shortest paths between every pair of nodes in a weighted graph using dynamic programming — how the recurrence works and when to use it.

The Lycoris Team The Lycoris Team · · 4 min read
A chalkboard covered in matrix notation and graph paths

The Floyd-Warshall algorithm computes the shortest path between every pair of vertices in a weighted graph in a single run, using dynamic programming instead of repeatedly solving a single-source shortest-path problem. It handles negative edge weights (as long as there’s no negative cycle) and runs in O(V³) time and O(V²) space, where V is the number of vertices.

The problem it solves

Algorithms like Dijkstra’s and Bellman-Ford find shortest paths from a single source vertex to every other vertex. If you need shortest paths between every pair of vertices, the naive approach is to run one of those algorithms once per vertex. Floyd-Warshall instead builds the full distance matrix directly, considering one candidate “intermediate” vertex at a time across the whole graph.

The core idea: intermediate vertices

Floyd-Warshall starts from the graph’s direct edges — the distance matrix initialized so that dist[i][j] is the weight of the edge from i to j if one exists, zero if i == j, and infinity otherwise. It then asks, for each vertex k in turn: does routing through k shorten any existing path?

The recurrence is:

dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

For every pair (i, j), this checks whether going from i to k and then k to j is shorter than the best path found so far. Run that check for every (i, j) pair while allowing k = 1, then again allowing k up to 2, and so on up through every vertex. By the time k has covered every vertex, dist[i][j] holds the true shortest path from i to j, allowed to route through any vertex in the graph.

Walking through the algorithm

for k in 1..n:
  for i in 1..n:
    for j in 1..n:
      if dist[i][k] + dist[k][j] < dist[i][j]:
        dist[i][j] = dist[i][k] + dist[k][j]

That triple-nested loop is the entire algorithm — no priority queue, no visited set, no per-source restart. Each pass over k adds one more vertex to the set of allowed intermediate stops, and the distance matrix only ever improves, never gets worse, because dist[i][j] starts as an upper bound (direct edge or infinity) and the recurrence only replaces it with something smaller.

Why it works

The correctness argument rests on optimal substructure: the shortest path from i to j that’s allowed to pass through vertices {1, ..., k} either doesn’t use vertex k at all (in which case it’s the same as the best path using {1, ..., k-1}), or it does use k, in which case it’s the best path from i to k plus the best path from k to j, both restricted to intermediate vertices {1, ..., k-1}. Since the loop processes k in increasing order and always has the correct restricted-path values from the previous iteration, each step’s answer is provably correct before moving on.

Negative edges and negative cycles

Floyd-Warshall handles negative edge weights correctly, which single-source algorithms like Dijkstra cannot (Dijkstra’s greedy vertex selection breaks down once a later, cheaper path can undercut an already-finalized one). Floyd-Warshall doesn’t have that problem because it doesn’t finalize vertices greedily — it revisits every pair on every pass.

It also detects negative cycles for free: after the algorithm finishes, check the diagonal of the distance matrix. If any dist[i][i] is negative, there’s a negative-weight cycle reachable from and back to vertex i, and the “shortest path” values touched by that cycle are meaningless (a cycle you can loop forever to keep lowering the cost).

Floyd-Warshall vs running Dijkstra from every vertex

Floyd-WarshallDijkstra from every source
Time complexityO(V³)O(V · (E log V)) with a binary heap
Negative edgesHandledNot supported
Negative cycle detectionBuilt in (check the diagonal)Not applicable
Best onDense graphs, or when V is smallSparse graphs, where E is much smaller than V²
ImplementationA few lines, no data structures neededNeeds a priority queue per run

On a dense graph, where E approaches V², the two approaches land in similar territory, but Floyd-Warshall’s simplicity — no priority queue, no per-source bookkeeping — makes it the more practical choice. On a sparse graph, running Dijkstra V times is asymptotically faster, since its cost scales with edges rather than the cube of vertices.

Where it shows up

Floyd-Warshall is a natural fit whenever you need a complete distance (or reachability) matrix rather than a single route: precomputing routing tables in small networks, computing the transitive closure of a graph (which vertices can reach which others at all, by treating edge weights as 1), and pathfinding on small, dense grids in games where every-pair distances get queried repeatedly. Its dynamic-programming structure is a good complement to reading about greedy algorithms and dynamic programming more broadly, and to graph traversal with BFS and DFS as the more basic building block those shortest-path algorithms build on. For a sense of why O(V³) is the complexity to expect from this style of triple-nested recurrence, see what Big O notation actually measures, and the master theorem for how divide-and-conquer algorithms get their own complexity bounds by comparison.

The takeaway

Floyd-Warshall fills in the full all-pairs shortest-path matrix by asking, for every candidate intermediate vertex in turn, whether routing through it beats the best path found so far. The triple-nested loop runs in O(V³) time, handles negative edges, and detects negative cycles by checking the matrix diagonal afterward — trading the asymptotic edge that per-source Dijkstra has on sparse graphs for simplicity and correctness guarantees that hold even when edge weights go negative.

The Lycoris Team The Lycoris Team · · 4 min read

The Rope Data Structure: Editing Huge Strings Efficiently

A rope is a binary tree of string chunks that makes inserting, deleting, and slicing huge strings fast, which is why text editors use it over plain arrays.

#Computer Science #Data Structures #Algorithms
The Lycoris Team The Lycoris Team · · 4 min read

Memoization vs. Tabulation in Dynamic Programming

Memoization caches results top-down via recursion; tabulation builds a table bottom-up with loops. Same technique, opposite direction, different tradeoffs.

#Computer Science #Algorithms #Data Structures
The Lycoris Team The Lycoris Team · · 4 min read

Manacher's Algorithm Explained

Manacher's algorithm finds the longest palindromic substring in linear time by reusing symmetry from palindromes already found, avoiding redundant checks.

#Computer Science #Algorithms #Data Structures