Builds on Dijkstra, which you haven’t finished yet.
Floyd-Warshall (All-Pairs Shortest Path)
You answer a list of yes/no questions about course order. Query [uj, vj] in queries asks whether course uj must be taken before course vj, and the answer is true exactly when a chain of pairs from prerequisites leads from uj to vj: [uj, x1], [x1, x2], ..., [xm, vj], where the single pair [uj, vj] is the shortest possible chain.
In prerequisites, a pair [ai, bi] means course ai comes before course bi. The courses are numbered 0 to numCourses - 1, and the pairs never form a cycle. Return the answers as a list of booleans, one per query, in the order of queries.
[1, 0] is true; no chain leads from course 0 to course 1, so [0, 1] is false.Floyd-Warshall
distance table ➔ allow one more middle node per round ➔ all shortest distances| 1 | from typing import List |
| 2 | |
| 3 | def floyd_warshall(n: int, edges: List[List[int]]) -> List[List[float]]: |
| 4 | """Floyd-Warshall (the core of Course Schedule IV): shortest distance between every |
| 5 | pair of nodes. |
| 6 | Time: O(V^3) | Space: O(V^2) |
| 7 | """ |
| 8 | dist = [[float("inf")] * n for _ in range(n)] |
| 9 | for i in range(n): |
| 10 | dist[i][i] = 0 # a node reaches itself at distance 0 |
| 11 | for u, v, w in edges: |
| 12 | # directed edge u -> v; keep the lighter of two parallel edges |
| 13 | dist[u][v] = min(dist[u][v], w) |
| 14 | for k in range(n): # k OUTERMOST: allow node k as a stop |
| 15 | for i in range(n): |
| 16 | for j in range(n): |
| 17 | if dist[i][k] + dist[k][j] < dist[i][j]: |
| 18 | dist[i][j] = dist[i][k] + dist[k][j] # go through k |
| 19 | return dist |
k, dist[i][j] is the length of the shortest path from i to j whose stops all lie among nodes 0..k.Allow the nodes as stops one at a time: for each k, let every pair i, j go through k when dist[i][k] + dist[k][j] < dist[i][j]. This is correct because, once k is allowed, the shortest i -> j path with stops in 0..k either avoids k, and is already in dist[i][j], or passes through k once and splits into an i -> k part and a k -> j part whose stops lie in 0..k - 1, and both of those are already in the table. The trap is the loop order: for k in range(n) must be the outermost loop. With k inside the i and j loops, dist[i][k] is read before the paths through the smaller stops have been allowed, and some pairs keep a distance that is too long, or no path at all.
See it run
Floyd-Warshall on 4 nodes, one k round at a time
k -dist[0] [0, 3, inf, inf]
Row i and column j are nodes. The cell in row i and column j is dist[i][j], the shortest known distance from node i to node j.
row/col kthe paths into and out of the middle node ki to kdist[i][k], the first part of the pathk to jdist[k][j], the second part of the pathnewdist[i][j], which gets a shorter valueupdateda cell already shortened in this round
dist[i][j] starts as the cost of the edge i to j, 0 on the diagonal, and inf where no edge exists. No middle node is allowed yet.
Gotcha 1 of 4
- The trap
Gotcha 1: Putting
for k in range(n)inside theiandjloopsMistakefor i in range(n): for j in range(n): for k in range(n):Fixfor k in range(n): for i in range(n): for j in range(n):What happens: With
kinnermost, the pair(i, j)is finished before the other pairs are updated.dist[i][k]may still miss a path through a smaller stop. So some pairs keep a distance that is too long, or no path at all.Example: n = 4, edges = [[0, 3, 1], [3, 2, 1], [2, 1, 1]], read dist[0][1]Mistake: returns infFix: returns 3
- Only the distances from one source are needed, as in Network Delay Time (LC 743): a heap from that source costs O((V + E) log V), far below V^3. See Dijkstra
- The path may use at most
kedges, as in Cheapest Flights Within K Stops (LC 787): the distance table has no edge count, so the cap needs rounds of relaxation instead. See Bellman-Ford - You need one order in which to take every course, as in Course Schedule II (LC 210), not an answer about every pair of courses. See Topological Sort
Deep Dive
Why one more middle node is enough
dist[i][j] is a table of shortest known distances. The outer loop allows one more node k as a middle stop in each round. The card's invariant says: after the round for k, dist[i][j] is the shortest path from i to j that uses only the nodes 0..k as stops.
Take the shortest path from i to j with stops in 0..k. Either it does not pass through k, or it does. If it does not, it is already in dist[i][j] from the earlier rounds.
If it passes through k, it passes once, because a shortest path does not repeat a node. Cut it at k. The part from i to k and the part from k to j both use only the stops 0..k - 1, so both are already in the table. The test dist[i][k] + dist[k][j] < dist[i][j] finds exactly this case.
Example: in the board below, the direct edge from node 0 to node 2 does not exist. In the round for k = 1, dist[0][1] + dist[1][2] = 3 + 1 = 4 gives dist[0][2] = 4.
One trace: one k round at a time
The board runs floyd_warshall on the first example of Find the City With the Smallest Number of Neighbors at a Threshold Distance (LC 1334): 4 cities and the roads 0-1:3, 1-2:1, 1-3:4 and 2-3:1. Each road works in both directions, so the board lists each road twice. The threshold is 4.
Round k = 0 changes nothing, because node 0 has only one neighbour. Round k = 1 joins node 0 to nodes 2 and 3 through node 1, so dist[0][2] = 4 and dist[0][3] = 7.
Round k = 2 reads those new values. It sets dist[0][3] = 4 + 1 = 5, and dist[1][3] = 1 + 1 = 2. Round k = 3 changes nothing.
The final table has row 0 as [0, 3, 4, 5]. The number of cities within distance 4 is [2, 3, 3, 2]. Cities 0 and 3 tie at 2, and the problem asks for the larger number, so the answer is 3.
The trap: the loop order
for k in range(n) must be the outermost loop. Round k reads values that the rounds for smaller stops must have finished.
With k innermost, the pair (i, j) is finished before the other pairs have been updated. Example: n = 4 and edges 0 -> 3, 3 -> 2, 2 -> 1, each of cost 1. The right value of dist[0][1] is 3. With i, j, k order, the result is infinity, because dist[0][1] is finished before dist[0][2] has a value.
Two smaller traps. Store a directed edge [u, v] in dist[u][v], not in dist[v][u]. And use min for parallel edges, so a lighter edge that comes first is not lost.
In Java or C++, do not use INT_MAX for no path. INT_MAX + 1 overflows to a negative number. Use INT_MAX / 2, or skip the pair.
The cost, and when to pick something else
Three nested loops over n nodes run n * n * n checks, so the time is O(V^3) and the table takes O(V^2) space. For 4 nodes that is 64 checks. For 100 nodes it is 1000000.
That is fine for a few hundred nodes at most. For a large sparse graph, running Dijkstra from every node (the cheapest-first card) costs about V times O((V + E) log V), which is much less.
If you need distances from only one source, as in Network Delay Time (LC 743), use Dijkstra. If the path may use at most k edges, as in Cheapest Flights Within K Stops (LC 787), use the rounds of Bellman-Ford: this table has no edge count.
If the question is only whether one node can reach another, as in Course Schedule IV (LC 1462), the same loops work with true and false in place of distances. If you need one order in which to take every course, as in Course Schedule II (LC 210), use Topological Sort. That answer is not about pairs.
60-Second Pitch
"I need the shortest distance between every pair of nodes on a small graph, so I use Floyd-Warshall. I keep a table dist, start it from the edges, and allow the nodes as middle stops one at a time. For each k, every pair i, j takes the path through k when dist[i][k] + dist[k][j] is shorter. It is correct because a shortest path with stops in 0..k either avoids k or passes through it once. Then both halves use only the earlier stops, and they are already in the table. That is why k must be the outermost loop. The time is O(V^3) and the space is O(V^2). For a large sparse graph, I would run Dijkstra from each node instead."
Common Questions
- Why must
kbe the outer loop? Round
kassumes the table already holds every shortest path with stops in0..k - 1. Withkinside the other loops, a pair is finished before the other pairs it depends on. Some distances stay too long or infinity.- What are the time and space complexity?
O(V^3) time, because there are three loops over
nnodes. O(V^2) space for the table. It suits a graph of a few hundred nodes at most.- How is it different from running Dijkstra from every node?
Dijkstra from every node costs about V times O((V + E) log V). That is less on a sparse graph, but it needs non-negative weights. Floyd-Warshall is shorter to write and allows negative weights, with no negative cycle.
- How do you tell that a graph has a negative cycle?
After the loops, check the diagonal. If
dist[i][i]is below 0 for somei, a cycle throughihas a negative total. Then the distances are not meaningful.- How do you use it only for reachability?
Replace the distances with true and false, and the test with
reach[i][k] and reach[k][j]. Setreach[i][j]to true when it holds. This is the idea behind Course Schedule IV (LC 1462).
SWE Reasoning
Spot it
- The question needs the shortest distance, or only reachability, between every pair of nodes.
- Or there are many queries about pairs of nodes, and the graph is small.
- The number of nodes is a few hundred at most, so V cubed is affordable.
- A threshold or a count per node is asked, such as the cities within a given distance.
- Weights can be negative, as long as no cycle has a negative total.
The invariant, step by step
dist[i][i] = 0,dist[u][v]is the lightest edge fromutov, and all other entries are infinity. No middle node is allowed yet.- The outer loop picks
k. From now on, nodekmay be a middle stop. - For every pair
i, j, test whetherdist[i][k] + dist[k][j]is less thandist[i][j]. If it is, take the shorter path. - A shortest path with stops in
0..keither avoidsk, so it is already indist[i][j], or it passes throughkonce and splits into two parts that use only0..k - 1. - Both parts are already in the table, so the test finds the best path. After the round for
k,dist[i][j]is the shortest path with stops in0..k. - After the last
k, every node was allowed, anddistholds all shortest distances.
Traps
knot outermost. Pairs are finished too early. Some distances are too long or infinity.INT_MAXfor no path in Java or C++. The sum overflows. Use half of the maximum, or skip unreached pairs.- Storing the edge backwards. A directed edge
[u, v]goes indist[u][v]. - Plain assignment for parallel edges. Use
min, so the lighter edge stays. - A large graph. O(V^3) is too slow beyond a few hundred nodes. Run Dijkstra from each node instead.
Practice
Builds on Dijkstra, which you haven’t finished yet.
Floyd-Warshall (All-Pairs Shortest Path)
You answer a list of yes/no questions about course order. Query [uj, vj] in queries asks whether course uj must be taken before course vj, and the answer is true exactly when a chain of pairs from prerequisites leads from uj to vj: [uj, x1], [x1, x2], ..., [xm, vj], where the single pair [uj, vj] is the shortest possible chain.
In prerequisites, a pair [ai, bi] means course ai comes before course bi. The courses are numbered 0 to numCourses - 1, and the pairs never form a cycle. Return the answers as a list of booleans, one per query, in the order of queries.
[1, 0] is true; no chain leads from course 0 to course 1, so [0, 1] is false.Floyd-Warshall
distance table ➔ allow one more middle node per round ➔ all shortest distances| 1 | from typing import List |
| 2 | |
| 3 | def floyd_warshall(n: int, edges: List[List[int]]) -> List[List[float]]: |
| 4 | """Floyd-Warshall (the core of Course Schedule IV): shortest distance between every |
| 5 | pair of nodes. |
| 6 | Time: O(V^3) | Space: O(V^2) |
| 7 | """ |
| 8 | dist = [[float("inf")] * n for _ in range(n)] |
| 9 | for i in range(n): |
| 10 | dist[i][i] = 0 # a node reaches itself at distance 0 |
| 11 | for u, v, w in edges: |
| 12 | # directed edge u -> v; keep the lighter of two parallel edges |
| 13 | dist[u][v] = min(dist[u][v], w) |
| 14 | for k in range(n): # k OUTERMOST: allow node k as a stop |
| 15 | for i in range(n): |
| 16 | for j in range(n): |
| 17 | if dist[i][k] + dist[k][j] < dist[i][j]: |
| 18 | dist[i][j] = dist[i][k] + dist[k][j] # go through k |
| 19 | return dist |
k, dist[i][j] is the length of the shortest path from i to j whose stops all lie among nodes 0..k.Allow the nodes as stops one at a time: for each k, let every pair i, j go through k when dist[i][k] + dist[k][j] < dist[i][j]. This is correct because, once k is allowed, the shortest i -> j path with stops in 0..k either avoids k, and is already in dist[i][j], or passes through k once and splits into an i -> k part and a k -> j part whose stops lie in 0..k - 1, and both of those are already in the table. The trap is the loop order: for k in range(n) must be the outermost loop. With k inside the i and j loops, dist[i][k] is read before the paths through the smaller stops have been allowed, and some pairs keep a distance that is too long, or no path at all.
See it run
Floyd-Warshall on 4 nodes, one k round at a time
k -dist[0] [0, 3, inf, inf]
Row i and column j are nodes. The cell in row i and column j is dist[i][j], the shortest known distance from node i to node j.
row/col kthe paths into and out of the middle node ki to kdist[i][k], the first part of the pathk to jdist[k][j], the second part of the pathnewdist[i][j], which gets a shorter valueupdateda cell already shortened in this round
dist[i][j] starts as the cost of the edge i to j, 0 on the diagonal, and inf where no edge exists. No middle node is allowed yet.
Gotcha 1 of 4
- The trap
Gotcha 1: Putting
for k in range(n)inside theiandjloopsMistakefor i in range(n): for j in range(n): for k in range(n):Fixfor k in range(n): for i in range(n): for j in range(n):What happens: With
kinnermost, the pair(i, j)is finished before the other pairs are updated.dist[i][k]may still miss a path through a smaller stop. So some pairs keep a distance that is too long, or no path at all.Example: n = 4, edges = [[0, 3, 1], [3, 2, 1], [2, 1, 1]], read dist[0][1]Mistake: returns infFix: returns 3
- Only the distances from one source are needed, as in Network Delay Time (LC 743): a heap from that source costs O((V + E) log V), far below V^3. See Dijkstra
- The path may use at most
kedges, as in Cheapest Flights Within K Stops (LC 787): the distance table has no edge count, so the cap needs rounds of relaxation instead. See Bellman-Ford - You need one order in which to take every course, as in Course Schedule II (LC 210), not an answer about every pair of courses. See Topological Sort
Deep Dive
Why one more middle node is enough
dist[i][j] is a table of shortest known distances. The outer loop allows one more node k as a middle stop in each round. The card's invariant says: after the round for k, dist[i][j] is the shortest path from i to j that uses only the nodes 0..k as stops.
Take the shortest path from i to j with stops in 0..k. Either it does not pass through k, or it does. If it does not, it is already in dist[i][j] from the earlier rounds.
If it passes through k, it passes once, because a shortest path does not repeat a node. Cut it at k. The part from i to k and the part from k to j both use only the stops 0..k - 1, so both are already in the table. The test dist[i][k] + dist[k][j] < dist[i][j] finds exactly this case.
Example: in the board below, the direct edge from node 0 to node 2 does not exist. In the round for k = 1, dist[0][1] + dist[1][2] = 3 + 1 = 4 gives dist[0][2] = 4.
One trace: one k round at a time
The board runs floyd_warshall on the first example of Find the City With the Smallest Number of Neighbors at a Threshold Distance (LC 1334): 4 cities and the roads 0-1:3, 1-2:1, 1-3:4 and 2-3:1. Each road works in both directions, so the board lists each road twice. The threshold is 4.
Round k = 0 changes nothing, because node 0 has only one neighbour. Round k = 1 joins node 0 to nodes 2 and 3 through node 1, so dist[0][2] = 4 and dist[0][3] = 7.
Round k = 2 reads those new values. It sets dist[0][3] = 4 + 1 = 5, and dist[1][3] = 1 + 1 = 2. Round k = 3 changes nothing.
The final table has row 0 as [0, 3, 4, 5]. The number of cities within distance 4 is [2, 3, 3, 2]. Cities 0 and 3 tie at 2, and the problem asks for the larger number, so the answer is 3.
The trap: the loop order
for k in range(n) must be the outermost loop. Round k reads values that the rounds for smaller stops must have finished.
With k innermost, the pair (i, j) is finished before the other pairs have been updated. Example: n = 4 and edges 0 -> 3, 3 -> 2, 2 -> 1, each of cost 1. The right value of dist[0][1] is 3. With i, j, k order, the result is infinity, because dist[0][1] is finished before dist[0][2] has a value.
Two smaller traps. Store a directed edge [u, v] in dist[u][v], not in dist[v][u]. And use min for parallel edges, so a lighter edge that comes first is not lost.
In Java or C++, do not use INT_MAX for no path. INT_MAX + 1 overflows to a negative number. Use INT_MAX / 2, or skip the pair.
The cost, and when to pick something else
Three nested loops over n nodes run n * n * n checks, so the time is O(V^3) and the table takes O(V^2) space. For 4 nodes that is 64 checks. For 100 nodes it is 1000000.
That is fine for a few hundred nodes at most. For a large sparse graph, running Dijkstra from every node (the cheapest-first card) costs about V times O((V + E) log V), which is much less.
If you need distances from only one source, as in Network Delay Time (LC 743), use Dijkstra. If the path may use at most k edges, as in Cheapest Flights Within K Stops (LC 787), use the rounds of Bellman-Ford: this table has no edge count.
If the question is only whether one node can reach another, as in Course Schedule IV (LC 1462), the same loops work with true and false in place of distances. If you need one order in which to take every course, as in Course Schedule II (LC 210), use Topological Sort. That answer is not about pairs.
60-Second Pitch
"I need the shortest distance between every pair of nodes on a small graph, so I use Floyd-Warshall. I keep a table dist, start it from the edges, and allow the nodes as middle stops one at a time. For each k, every pair i, j takes the path through k when dist[i][k] + dist[k][j] is shorter. It is correct because a shortest path with stops in 0..k either avoids k or passes through it once. Then both halves use only the earlier stops, and they are already in the table. That is why k must be the outermost loop. The time is O(V^3) and the space is O(V^2). For a large sparse graph, I would run Dijkstra from each node instead."
Common Questions
- Why must
kbe the outer loop? Round
kassumes the table already holds every shortest path with stops in0..k - 1. Withkinside the other loops, a pair is finished before the other pairs it depends on. Some distances stay too long or infinity.- What are the time and space complexity?
O(V^3) time, because there are three loops over
nnodes. O(V^2) space for the table. It suits a graph of a few hundred nodes at most.- How is it different from running Dijkstra from every node?
Dijkstra from every node costs about V times O((V + E) log V). That is less on a sparse graph, but it needs non-negative weights. Floyd-Warshall is shorter to write and allows negative weights, with no negative cycle.
- How do you tell that a graph has a negative cycle?
After the loops, check the diagonal. If
dist[i][i]is below 0 for somei, a cycle throughihas a negative total. Then the distances are not meaningful.- How do you use it only for reachability?
Replace the distances with true and false, and the test with
reach[i][k] and reach[k][j]. Setreach[i][j]to true when it holds. This is the idea behind Course Schedule IV (LC 1462).
SWE Reasoning
Spot it
- The question needs the shortest distance, or only reachability, between every pair of nodes.
- Or there are many queries about pairs of nodes, and the graph is small.
- The number of nodes is a few hundred at most, so V cubed is affordable.
- A threshold or a count per node is asked, such as the cities within a given distance.
- Weights can be negative, as long as no cycle has a negative total.
The invariant, step by step
dist[i][i] = 0,dist[u][v]is the lightest edge fromutov, and all other entries are infinity. No middle node is allowed yet.- The outer loop picks
k. From now on, nodekmay be a middle stop. - For every pair
i, j, test whetherdist[i][k] + dist[k][j]is less thandist[i][j]. If it is, take the shorter path. - A shortest path with stops in
0..keither avoidsk, so it is already indist[i][j], or it passes throughkonce and splits into two parts that use only0..k - 1. - Both parts are already in the table, so the test finds the best path. After the round for
k,dist[i][j]is the shortest path with stops in0..k. - After the last
k, every node was allowed, anddistholds all shortest distances.
Traps
knot outermost. Pairs are finished too early. Some distances are too long or infinity.INT_MAXfor no path in Java or C++. The sum overflows. Use half of the maximum, or skip unreached pairs.- Storing the edge backwards. A directed edge
[u, v]goes indist[u][v]. - Plain assignment for parallel edges. Use
min, so the lighter edge stays. - A large graph. O(V^3) is too slow beyond a few hundred nodes. Run Dijkstra from each node instead.