AlgoMaster Logo

Floyd-Warshall Algorithm

Low Priority12 min readUpdated July 4, 2026
Listen to this chapter
Unlock Audio

The Floyd-Warshall algorithm computes the shortest path between every pair of vertices in a weighted directed graph in a single run. Dijkstra and Bellman-Ford solve the single-source variant, finding shortest paths from one fixed vertex; Floyd-Warshall solves the all-pairs variant, producing a full V x V distance matrix. It is a dynamic programming algorithm that runs in O(V^3) and fits into about 10 lines of code.

This chapter covers how the algorithm works, why it works, how to detect negative cycles, how to reconstruct actual paths, and when to use Floyd-Warshall versus alternatives like Dijkstra or Bellman-Ford.

What Is Floyd-Warshall?

The result is a V x V distance matrix where entry dist[i][j] holds the shortest path length from vertex i to vertex j.

Its main properties:

  • Works with negative edge weights. Unlike Dijkstra, Floyd-Warshall handles negative weights correctly, as long as there are no negative-weight cycles.
  • Detects negative cycles. If a negative cycle exists, the algorithm can identify it.
  • Uses dynamic programming. The algorithm builds solutions incrementally by considering more and more intermediate vertices.
  • Time complexity is O(V^3). This makes it practical only for graphs with a relatively small number of vertices (up to a few hundred).

Floyd-Warshall is named after Robert Floyd and Stephen Warshall, who independently published related algorithms in 1962. Warshall's version solved transitive closure (reachability), while Floyd's version handled shortest paths.

The Key Idea

The core insight behind Floyd-Warshall is this. For every pair of vertices (i, j), ask this question:

If yes, update the shortest distance. If not, leave it alone.

The formula is:

The algorithm tries every possible intermediate vertex k, one at a time. After considering all V vertices as potential intermediates, the distance matrix contains the true shortest paths.

Why This Works

The shortest path from vertex i to vertex j either:

  1. Goes directly from i to j (or through intermediates already considered), or
  2. Goes from i to some vertex k, then from k to j.

By iterating k over all vertices, the algorithm exhaustively checks every possible "shortcut" through the graph. The dynamic programming structure guarantees that by the time vertex k is considered as an intermediate, the optimal distances using only vertices {0, 1, ..., k-1} as intermediates are already computed.

Consider three vertices where the direct path from A to C costs 10, but going through B costs 3 + 4 = 7.

When the algorithm considers B as an intermediate vertex, it finds that dist[A][B] + dist[B][C] = 3 + 4 = 7 < 10 = dist[A][C], so it updates dist[A][C] to 7.

Implementation

The complete Floyd-Warshall implementation:

Initialization

The input graph is a V x V adjacency matrix where:

  • graph[i][j] is the weight of the edge from i to j.
  • graph[i][i] = 0 (distance from a vertex to itself is zero).
  • graph[i][j] = INF if there is no direct edge from i to j.

This is copied into a dist matrix. As the algorithm runs, dist[i][j] gradually converges to the true shortest path.

Why k Must Be the Outer Loop

The loop order is k (outer), i (middle), j (inner), and the k loop must be outermost.

The dynamic programming state is defined as: dist_k[i][j] = shortest path from i to j using only vertices {0, 1, ..., k} as intermediates. Computing dist_k[i][j] requires dist_{k-1}[i][k] and dist_{k-1}[k][j], which means all updates for intermediate vertices 0 through k-1 must already be complete.

Putting i or j as the outer loop would update distances for different intermediates in a jumbled order, and the DP recurrence breaks. The k-outer order is what makes the DP correct.

Why In-Place Update Works

The DP formulation defines two layers: dist_{k-1} (the previous layer) and dist_k (the new layer being computed). A textbook implementation would keep both matrices and write into a fresh dist_k. The code above uses a single dist matrix and overwrites in place, and the in-place update produces exactly the same values.

The reason is that the two source values dist[i][k] and dist[k][j] do not change during iteration k. Consider what happens during their updates:

  • dist[i][k] in iteration k would update to min(dist[i][k], dist[i][k] + dist[k][k]). Since dist[k][k] = 0, the candidate equals dist[i][k], so the value is unchanged.
  • dist[k][j] would update to min(dist[k][j], dist[k][k] + dist[k][j]). Same reasoning, same no-op.

Because the k-th row and k-th column are stable during iteration k, reading them in place is identical to reading them from a separate dist_{k-1} matrix. This saves a full V x V matrix of memory at no cost to correctness. The argument relies on dist[k][k] = 0, which is why the initialization step matters.

Why Not Use the Language's Max Integer for INF?

The code uses 100000000 (10^8) instead of the maximum integer constant (such as Integer.MAX_VALUE in Java, INT_MAX in C++, or int.MaxValue in C#). This is deliberate. When the algorithm computes dist[i][k] + dist[k][j], if both values are the max integer, the addition overflows to a negative number. That negative result would incorrectly "win" the min comparison and corrupt the distance matrix.

Using a large but safe value avoids this trap entirely. Choose a value large enough that no real shortest path would reach it, but small enough that adding two of them does not overflow.

Step-by-Step Walkthrough

Here is a trace of Floyd-Warshall on a small graph with 4 vertices (0, 1, 2, 3).

Edges: (0->1, weight 3), (0->2, weight 7), (1->2, weight 2), (1->3, weight 5), (2->3, weight 1), (3->0, weight 2).

Initial Distance Matrix

Fill the matrix with direct edge weights. Missing edges get INF, and the diagonal is 0.

0123
0037INF
1INF025
2INFINF01
32INFINF0

k = 0 (Intermediate vertex: 0)

For each pair (i, j), check if going through vertex 0 improves the path.

  • dist[3][1]: Can 3->0->1 improve on INF? dist[3][0] + dist[0][1] = 2 + 3 = 5 < INF. Yes. Update to 5.
  • dist[3][2]: Can 3->0->2 improve on INF? dist[3][0] + dist[0][2] = 2 + 7 = 9 < INF. Yes. Update to 9.
0123
0037INF
1INF025
2INFINF01
32590

k = 1 (Intermediate vertex: 1)

  • dist[0][2]: Can 0->1->2 improve on 7? dist[0][1] + dist[1][2] = 3 + 2 = 5 < 7. Yes. Update to 5.
  • dist[0][3]: Can 0->1->3 improve on INF? dist[0][1] + dist[1][3] = 3 + 5 = 8 < INF. Yes. Update to 8.
  • dist[3][2]: Can 3->1->2 improve on 9? dist[3][1] + dist[1][2] = 5 + 2 = 7 < 9. Yes. Update to 7.
  • dist[3][3]: Can 3->1->3 improve on 0? dist[3][1] + dist[1][3] = 5 + 5 = 10 > 0. No.
0123
00358
1INF025
2INFINF01
32570

k = 2 (Intermediate vertex: 2)

  • dist[0][3]: Can 0->2->3 improve on 8? dist[0][2] + dist[2][3] = 5 + 1 = 6 < 8. Yes. Update to 6.
  • dist[1][3]: Can 1->2->3 improve on 5? dist[1][2] + dist[2][3] = 2 + 1 = 3 < 5. Yes. Update to 3.
  • dist[3][3]: Can 3->2->3 improve on 0? dist[3][2] + dist[2][3] = 7 + 1 = 8 > 0. No.
0123
00356
1INF023
2INFINF01
32570

k = 3 (Intermediate vertex: 3)

  • dist[0][0]: Can 0->3->0 improve on 0? dist[0][3] + dist[3][0] = 6 + 2 = 8 > 0. No.
  • dist[1][0]: Can 1->3->0 improve on INF? dist[1][3] + dist[3][0] = 3 + 2 = 5 < INF. Yes. Update to 5.
  • dist[1][1]: Can 1->3->1 improve on 0? dist[1][3] + dist[3][1] = 3 + 5 = 8 > 0. No.
  • dist[2][0]: Can 2->3->0 improve on INF? dist[2][3] + dist[3][0] = 1 + 2 = 3 < INF. Yes. Update to 3.
  • dist[2][1]: Can 2->3->1 improve on INF? dist[2][3] + dist[3][1] = 1 + 5 = 6 < INF. Yes. Update to 6.

Final Distance Matrix:

0123
00356
15023
23601
32570

Every cell now contains the shortest distance between that pair of vertices. For example, the shortest path from vertex 1 to vertex 0 is 5 (going 1->3->0, with cost 3 + 2).

Negative Cycle Detection

Floyd-Warshall handles negative edge weights correctly. The trouble starts when there is a negative cycle, a cycle whose total weight is negative. If such a cycle exists, the concept of "shortest path" becomes meaningless, since looping around the cycle indefinitely reduces the distance without bound.

After running the algorithm, check the diagonal of the distance matrix. If dist[i][i] < 0 for any vertex i, then vertex i lies on a negative cycle.

Why? The value dist[i][i] represents the shortest "path" from vertex i back to itself. Normally that is 0 (the empty path). But if there is a negative cycle reachable from i that loops back to i, the algorithm will find that going around the cycle gives a cost less than 0.

In the graph above, the cycle A -> B -> C -> A has total weight 2 + (-5) + 1 = -2. After Floyd-Warshall runs, dist[A][A], dist[B][B], and dist[C][C] will all be negative, signaling that each of these vertices is part of a negative cycle.

Path Reconstruction

Knowing the shortest distance is useful, but many problems also require the actual path. Floyd-Warshall supports this with a next matrix (sometimes called the predecessor or parent matrix).

Alongside the distance matrix, maintain a matrix next[i][j] that stores the next vertex to visit on the shortest path from i to j. When dist[i][j] is updated because going through k is shorter, also update next[i][j] = next[i][k], meaning "to go from i to j, first take the same step as going from i to k."

Using the graph from our earlier walkthrough, calling getPath(1, 0) would return [1, 3, 0], which corresponds to the shortest path of cost 5.

When to Use Floyd-Warshall

Its O(V^3) time complexity makes it impractical for large graphs. For small or dense graphs, its simplicity is a strong advantage.

Floyd-Warshall vs. Running Dijkstra V Times

The two common approaches to all-pairs shortest paths compare as follows:

CriteriaFloyd-WarshallDijkstra (V times)
Time complexityO(V^3)O(V * (V + E) * log V) with min-heap
Negative weightsYes (no negative cycles)No
Implementation~10 lines, simpleMore code, need priority queue
Best forDense graphs, small VSparse graphs, large V
SpaceO(V^2)O(V^2) for all results

For a dense graph where E is close to V^2, Floyd-Warshall and running Dijkstra V times have similar performance. Floyd-Warshall is simpler to implement and debug. For sparse graphs with many vertices, Dijkstra V times wins.

Good Fits for Floyd-Warshall

  • Small graphs (V <= 400 or so). The cubic time is manageable.
  • Dense graphs. When most vertices are connected, the adjacency matrix representation is natural.
  • Negative weights. Dijkstra cannot handle these.
  • Problems asking for all-pairs results. Any problem that needs the shortest path between every pair.
  • Transitive closure. A boolean variant (can i reach j?) solves reachability problems.

When to Avoid It

  • Single-source shortest path. Use Dijkstra or Bellman-Ford instead.
  • Large graphs. If V > 1000, O(V^3) is too slow for most competitive programming and interview time limits.
  • Sparse graphs with many vertices. Running Dijkstra from each vertex is more efficient.

Complexity Analysis

Time Complexity: O(V^3)

The algorithm has three nested loops, each iterating V times. Every cell in the V x V matrix is examined V times (once for each intermediate vertex k).

For V = 100, that is 10^6 operations, which is instant. For V = 500, that is 1.25 * 10^8, which is borderline. For V = 1000, that is 10^9, which is too slow for most judges.

Space Complexity: O(V^2)

The distance matrix requires V^2 space. If path reconstruction is needed, the next matrix adds another V^2. The space is dominated by these matrices, not the algorithm's logic.

When Is O(V^3) Acceptable?

VOperationsVerdict
10010^6Very fast
2008 * 10^6Fast
4006.4 * 10^7Fine
5001.25 * 10^8Tight
100010^9Too slow

In most competitive programming settings, V <= 400 is the practical limit for Floyd-Warshall.

Key Takeaways

  • Floyd-Warshall computes all-pairs shortest paths in a single run using dynamic programming, producing a V x V distance matrix where dist[i][j] holds the shortest distance from i to j.
  • The algorithm tries every vertex k as an intermediate point and applies dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]), and the k loop must be the outermost of the three nested loops for the recurrence to stay correct.
  • It handles negative edge weights correctly as long as no negative cycle exists, and it detects a negative cycle when any diagonal entry dist[i][i] becomes less than 0.
  • You can recover the actual shortest path by maintaining a next matrix that records the first hop from i toward j, updating next[i][j] = next[i][k] whenever the distance through k improves.
  • The algorithm runs in O(V^3) time and uses O(V^2) space, so it is practical only for small graphs where V is roughly 400 to 500 or fewer.
  • For sparse graphs with many vertices, running Dijkstra from each vertex is faster, while Floyd-Warshall is a strong fit for small or dense graphs and for graphs with negative weights that Dijkstra cannot handle.

Quiz

Floyd-Warshall Algorithm Quiz

10 quizzes