Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA)  Module 12 – Shortest paths, spanning trees and flows

All-pairs shortest paths: Floyd-Warshall

How Floyd-Warshall fills the distance table of every pair in O(V³) by allowing one more intermediate vertex per round, and how it flags negative cycles.

  • Intermediate
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free core, Pro depth: 11 of 35 minutes free
Free: One more allowed stop per round · A latency table for six data centres · The code and its cost · 3 more sections. With a pass: 3 in-depth parts and 3 interview answers, about 24 minutes.

What you will learn

  • Implement Floyd-Warshall in O(V³) time and O(V²) memory
  • Detect negative cycles from the diagonal of the table
  • Compute reachability with Warshall's transitive closure
  • Choose between Floyd-Warshall, Dijkstra from every vertex and Johnson's algorithm

Before you start

The all-pairs shortest-path problem asks for the shortest distance between every ordered pair of vertices, a whole table instead of one row. Floyd-Warshall fills that table with one short triple loop. It starts from the direct edges and then, for k = 0, 1, …, V − 1, allows vertex k as an extra stop on every path: dist[i][j] becomes dist[i][k] + dist[k][j] whenever that is shorter. After the last round every path may stop anywhere, so every entry is a shortest distance. It takes O(V³) time and O(V²) memory, works with negative edges, and reports a negative cycle as a negative entry on the diagonal. Robert Floyd published the shortest-path version in 1962, and Stephen Warshall published the same loop over true and false that year, for reachability. On sparse graphs, Dijkstra’s algorithm from every vertex, or Johnson’s algorithm when some weights are negative, does the same job faster.

One more allowed stop per round

Number the vertices 0 to V − 1 and keep a V × V table:

  1. Start with 0 on the diagonal, the weight of the cheapest direct edge from i to j where there is one, and infinity everywhere else.
  2. For each k in turn, the outer loop: for every pair (i, j), if dist[i][k] + dist[k][j] < dist[i][j], store the smaller value.

After round k, dist[i][j] is the shortest distance among the paths whose intermediate stops all lie among vertices 0 to k. Round k only asks whether going through k helps, given the best paths that avoid every vertex after k. The loop order is the whole algorithm: k must be the outer loop.

A latency table for six data centres

Six data centres A to F joined by twelve one-way links with latencies; the fastest route from A to F is thick.ABCDEF4325171298263

Six data centres and their one-way links

Text description of the diagram

Each arrow is a one-way link, labelled with its latency in milliseconds.

  1. A ring of links: A to B 4, B to C 3, C to D 2, D to E 5, E to F 1 and F to A 7.
  2. Six more links: A to D 12, B to E 9, C to F 8, E to B 2, D to A 6 and F to C 3.
  3. The thick links are the fastest route from A to F: A to B, B to E and E to F, 4 + 9 + 1 = 14 milliseconds. No pair of centres is further apart, so A to F is the slowest pair in the finished table.
The table after each round, with improved entries marked Python · table/table_after_k.py
# Floyd-Warshall on six made-up data centres, A to F, with one-way network latencies in milliseconds. The table is
# printed after each k: round k lets paths pass through centre k, and a * marks every entry that round improved.
import math

names = "ABCDEF"
links = [("A", "B", 4), ("B", "C", 3), ("C", "D", 2), ("D", "E", 5), ("E", "F", 1), ("F", "A", 7),
         ("A", "D", 12), ("B", "E", 9), ("C", "F", 8), ("E", "B", 2), ("D", "A", 6), ("F", "C", 3)]
n = len(names)
dist = [[0 if i == j else math.inf for j in range(n)] for i in range(n)]
for a, b, ms in links:
    dist[names.index(a)][names.index(b)] = ms


def show(title, changed=frozenset()):
    print(title)
    print("     " + "".join(f"{c:>5}" for c in names))
    for i in range(n):
        cells = []
        for j in range(n):
            text = "-" if dist[i][j] == math.inf else str(dist[i][j])
            cells.append(f"{text + ('*' if (i, j) in changed else ''):>5}")
        print(f"  {names[i]}  " + "".join(cells))


show("direct links only (- means no link):")
for k in range(n):
    changed = set()
    for i in range(n):
        for j in range(n):
            if dist[i][k] + dist[k][j] < dist[i][j]:
                dist[i][j] = dist[i][k] + dist[k][j]
                changed.add((i, j))
    show(f"after k = {names[k]}: paths may also pass through {names[: k + 1]} ({len(changed)} improved)", changed)

Output

direct links only (- means no link):
         A    B    C    D    E    F
  A      0    4    -   12    -    -
  B      -    0    3    -    9    -
  C      -    -    0    2    -    8
  D      6    -    -    0    5    -
  E      -    2    -    -    0    1
  F      7    -    3    -    -    0
after k = A: paths may also pass through A (3 improved)
         A    B    C    D    E    F
  A      0    4    -   12    -    -
  B      -    0    3    -    9    -
  C      -    -    0    2    -    8
  D      6  10*    -    0    5    -
  E      -    2    -    -    0    1
  F      7  11*    3  19*    -    0
after k = B: paths may also pass through AB (5 improved)
         A    B    C    D    E    F
  A      0    4   7*   12  13*    -
  B      -    0    3    -    9    -
  C      -    -    0    2    -    8
  D      6   10  13*    0    5    -
  E      -    2   5*    -    0    1
  F      7   11    3   19  20*    0
after k = C: paths may also pass through ABC (7 improved)
         A    B    C    D    E    F
  A      0    4    7   9*   13  15*
  B      -    0    3   5*    9  11*
  C      -    -    0    2    -    8
  D      6   10   13    0    5  21*
  E      -    2    5   7*    0    1
  F      7   11    3   5*   20    0
after k = D: paths may also pass through ABCD (6 improved)
         A    B    C    D    E    F
  A      0    4    7    9   13   15
  B    11*    0    3    5    9   11
  C     8*  12*    0    2   7*    8
  D      6   10   13    0    5   21
  E    13*    2    5    7    0    1
  F      7   11    3    5  10*    0
after k = E: paths may also pass through ABCDE (6 improved)
         A    B    C    D    E    F
  A      0    4    7    9   13  14*
  B     11    0    3    5    9  10*
  C      8   9*    0    2    7    8
  D      6   7*  10*    0    5   6*
  E     13    2    5    7    0    1
  F      7   11    3    5   10    0
after k = F: paths may also pass through ABCDEF (4 improved)
         A    B    C    D    E    F
  A      0    4    7    9   13   14
  B     11    0    3    5    9   10
  C      8    9    0    2    7    8
  D      6    7   9*    0    5    6
  E     8*    2   4*   6*    0    1
  F      7   11    3    5   10    0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 table_after_k.py

The table started with the 12 direct links and 18 gaps. Allowing A as a stop filled 3 entries, such as D to B through A in 10 ms. Allowing B gave A to C in 7, and allowing C shortened A to D from the direct 12 to 9, through B and C. Some entries improved twice: E to A was 13 after round D and 8 after round F, through F. After round F every gap was filled, and the slowest pair, A to F, takes 14 ms.

The code and its cost

Floyd-Warshall in Python, with the slowest pair Python · core/floyd_warshall.py
# Floyd-Warshall: k is the outer loop, so round k lets every path also pass through vertex k. O(V^3) time and
# O(V^2) memory; negative edges are fine, and a negative cycle shows up as a negative entry on the diagonal.
import math


def floyd_warshall(n, edges):
    """Edges (u, v, w) on vertices 0..n-1. Returns the distance table, or None if the graph has a negative cycle."""
    dist = [[0 if i == j else math.inf for j in range(n)] for i in range(n)]
    for u, v, w in edges:
        dist[u][v] = min(dist[u][v], w)  # keep the cheapest of parallel edges
    for k in range(n):
        row_k = dist[k]
        for i in range(n):
            d_ik = dist[i][k]
            if d_ik == math.inf:
                continue  # i cannot reach k: nothing to gain through k
            row_i = dist[i]
            for j in range(n):
                if d_ik + row_k[j] < row_i[j]:
                    row_i[j] = d_ik + row_k[j]
    if any(dist[i][i] < 0 for i in range(n)):
        return None
    return dist


names = "ABCDEF"
links = [("A", "B", 4), ("B", "C", 3), ("C", "D", 2), ("D", "E", 5), ("E", "F", 1), ("F", "A", 7),
         ("A", "D", 12), ("B", "E", 9), ("C", "F", 8), ("E", "B", 2), ("D", "A", 6), ("F", "C", 3)]
table = floyd_warshall(6, [(names.index(a), names.index(b), ms) for a, b, ms in links])
print("from  " + " ".join(f"{c:>3}" for c in names))
for i, row in enumerate(table):
    print(f"  {names[i]}   " + " ".join(f"{d:>3}" for d in row))
worst = max((table[i][j], names[i], names[j]) for i in range(6) for j in range(6))
print(f"the slowest pair: {worst[1]} to {worst[2]}, {worst[0]} ms")

Output

from    A   B   C   D   E   F
  A     0   4   7   9  13  14
  B    11   0   3   5   9  10
  C     8   9   0   2   7   8
  D     6   7   9   0   5   6
  E     8   2   4   6   0   1
  F     7  11   3   5  10   0
the slowest pair: A to F, 14 ms

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 floyd_warshall.py

The three loops run V³ times: 6.4 × 10⁷ steps for V = 400 and 10⁹ for V = 1,000. The table holds V² numbers, which is 800 MB of 8-byte integers for V = 10⁴, before any per-entry overhead of the language. The if d_ik == inf test skips rows that cannot reach k, which saves work on sparse graphs without changing the answer. A second table of next hops, filled in the same loop, rebuilds the route of every pair.

Negative edges and the diagonal

A negative edge, then a negative cycle, seen on the diagonal Python · negative/diagonal.py
# Negative edges in Floyd-Warshall: fine on their own, but a negative cycle makes the diagonal negative, because a
# vertex on it can reach itself for less than nothing.
import math


def floyd_warshall(n, edges):
    dist = [[0 if i == j else math.inf for j in range(n)] for i in range(n)]
    for u, v, w in edges:
        dist[u][v] = min(dist[u][v], w)
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    return dist


names = "PQRS"
for label, s_to_q in (("S -> Q costs 1, so the cycle Q, R, S weighs +1", 1), ("S -> Q costs -1, so the cycle Q, R, S weighs -1", -1)):
    edges = [(0, 1, 3), (1, 2, -2), (2, 3, 2), (3, 1, s_to_q)]
    dist = floyd_warshall(4, edges)
    print(f"{label}:")
    print("  P to S:", dist[0][3], "  diagonal:", [dist[i][i] for i in range(4)])

Output

S -> Q costs 1, so the cycle Q, R, S weighs +1:
  P to S: 3   diagonal: [0, 0, 0, 0]
S -> Q costs -1, so the cycle Q, R, S weighs -1:
  P to S: 2   diagonal: [0, -1, -1, -2]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 diagonal.py

With the cycle Q, R, S weighing +1, the negative edge Q → R simply made P to S cost 3 and every diagonal entry stayed 0. Once the cycle weighed −1, each of its vertices could reach itself for less than nothing, so its diagonal entries turned negative. That is the test: after the last round, the graph has a negative cycle exactly when some dist[i][i] is below 0, and then the other entries cannot be trusted as distances.

Choosing an all-pairs method

Method Time Use it for
Floyd-Warshall O(V³) Dense graphs, or V up to a few hundred; negative edges allowed, negative cycles found
Dijkstra from every vertex O(V (V + E) log V) Sparse graphs with no negative weight
Johnson’s algorithm O(VE log V) Sparse graphs with negative edges; negative cycles found
BFS from every vertex O(V (V + E)) Graphs without weights

Johnson’s algorithm reweights the edges with potentials from one Bellman-Ford run, so that Dijkstra’s algorithm can then run from every vertex. Warshall’s transitive closure is the same triple loop over true and false, and it answers which vertex can reach which.

Exercise and self-check

Return the table of all shortest distances, or a cycle alarm. The largest sample test has 120 vertices.

Exercise · Medium · Python, JavaScript

The table of all shortest distances, or a cycle alarm

A directed graph has vertices 0 to n - 1 and edges (u, v, w) (JavaScript: three-element arrays) whose weights may be negative; there may be several edges between the same two vertices, and loops from a vertex to itself. Write all_pairs(n, edges) (JavaScript: allPairs(n, edges)), which returns the n × n table of shortest distances as a list of rows, where table[i][j] is the shortest distance from i to j, 0 on the diagonal, and None (JavaScript: null) when j cannot be reached from i.

If the graph contains a cycle of negative total weight anywhere, some pairs have no shortest distance: return None (JavaScript: null) instead of the table. The tests include the lesson's six data centres, negative edges without a negative cycle, a negative loop on one vertex, 300 small random graphs checked against every path and every cycle, and graphs of 120 vertices whose distances are known in advance, with and without one negative cycle.

Python · Starter code · all_pairs.py

def all_pairs(n, edges):
    """Edges (u, v, w), any sign. The table of shortest distances (None if unreachable), or None if a negative cycle exists."""
    raise NotImplementedError("write all_pairs")
The sample tests · test_all_pairs.py
import random

from all_pairs import all_pairs

DATA_CENTRES = [(0, 1, 4), (1, 2, 3), (2, 3, 2), (3, 4, 5), (4, 5, 1), (5, 0, 7), (0, 3, 12), (1, 4, 9), (2, 5, 8),
                (4, 1, 2), (3, 0, 6), (5, 2, 3)]


def by_every_path(n, edges):
    """The slow answer: None if any cycle is negative, else the cheapest simple path for every pair."""
    out = [[] for _ in range(n)]
    for u, v, w in edges:
        out[u].append((v, w))
    table = [[0 if i == j else None for j in range(n)] for i in range(n)]
    for s in range(n):
        stack = [(s, 0, {s})]
        while stack:
            u, total, seen = stack.pop()
            for v, w in out[u]:
                if v == s and total + w < 0:
                    return None  # a negative cycle through s
                if v not in seen:
                    if table[s][v] is None or total + w < table[s][v]:
                        table[s][v] = total + w
                    stack.append((v, total + w, seen | {v}))
    return table


def test_data_centres():
    """the lesson's six data centres"""
    assert all_pairs(6, DATA_CENTRES) == [
        [0, 4, 7, 9, 13, 14], [11, 0, 3, 5, 9, 10], [8, 9, 0, 2, 7, 8],
        [6, 7, 9, 0, 5, 6], [8, 2, 4, 6, 0, 1], [7, 11, 3, 5, 10, 0],
    ]


def test_negative_edges_without_a_negative_cycle():
    """Q, R, S form a cycle of +1 with a negative edge in it; nothing leads back to P"""
    edges = [(0, 1, 3), (1, 2, -2), (2, 3, 2), (3, 1, 1)]
    assert all_pairs(4, edges) == [[0, 3, 1, 3], [None, 0, -2, 0], [None, 3, 0, 2], [None, 1, -1, 0]]


def test_negative_cycle():
    """S -> Q of -1 makes the cycle Q, R, S weigh -1"""
    assert all_pairs(4, [(0, 1, 3), (1, 2, -2), (2, 3, 2), (3, 1, -1)]) is None


def test_negative_loop():
    """a loop of -1 on vertex 1 is a negative cycle, even though nothing else is wrong"""
    assert all_pairs(3, [(0, 1, 2), (1, 2, 2), (1, 1, -1)]) is None


def test_parallel_edges_and_a_positive_loop():
    """two edges from 0 to 1 (5 and 2) and a loop of +4 on vertex 1"""
    assert all_pairs(2, [(0, 1, 5), (0, 1, 2), (1, 1, 4), (1, 0, 1)]) == [[0, 2], [1, 0]]


def test_random_graphs():
    """300 small random graphs with weights from -3 to 9, against every path and every cycle"""
    rng = random.Random(23)
    for _ in range(300):
        n = rng.randint(1, 5)
        edges = [(rng.randrange(n), rng.randrange(n), rng.randint(-3, 9)) for _ in range(rng.randint(0, 9))]
        assert all_pairs(n, edges) == by_every_path(n, edges), (n, edges)


def line_graph(rng, connected, isolated):
    """Vertices on a line, each edge as long as the gap it spans, then potentials: every distance is known."""
    n = connected + isolated
    labels = list(range(n))
    rng.shuffle(labels)
    on_line, alone = labels[:connected], labels[connected:]
    x = dict(zip(on_line, sorted(rng.sample(range(100_000), connected))))
    edges = []
    for a, b in zip(on_line, on_line[1:]):
        edges += [(a, b, x[b] - x[a]), (b, a, x[b] - x[a])]
    for _ in range(1_000):
        a, b = rng.choice(on_line), rng.choice(on_line)
        edges.append((a, b, abs(x[a] - x[b]) + rng.randint(0, 50)))
    p = {v: rng.randint(-5_000, 5_000) for v in range(n)}
    edges = [(u, v, w + p[u] - p[v]) for u, v, w in edges]
    table = [[0 if i == j else None for j in range(n)] for i in range(n)]
    for a in on_line:
        for b in on_line:
            if a != b:
                table[a][b] = abs(x[a] - x[b]) + p[a] - p[b]
    return edges, table, on_line


def test_large_graph():
    """120 vertices, about 1,200 edges, many of them negative, and 5 vertices with no edges at all"""
    rng = random.Random(29)
    edges, table, _ = line_graph(rng, 115, 5)
    rng.shuffle(edges)
    assert all_pairs(120, edges) == table


def test_large_graph_with_one_negative_cycle():
    """the same kind of graph, plus one edge that closes a cycle of weight -1"""
    rng = random.Random(31)
    edges, table, on_line = line_graph(rng, 115, 5)
    a, b = on_line[3], on_line[90]
    edges.append((b, a, -table[a][b] - 1))  # a reaches b at table[a][b], so a -> ... -> b -> a weighs -1
    rng.shuffle(edges)
    assert all_pairs(120, edges) is None

JavaScript · Starter code · all_pairs.mjs

/** Edges [u, v, w], any sign. The table of shortest distances (null if unreachable), or null if a negative cycle exists. */
export function allPairs(n, edges) {
  throw new Error('write allPairs');
}
The sample tests · all_pairs.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { allPairs } from './all_pairs.mjs';

const DATA_CENTRES = [[0, 1, 4], [1, 2, 3], [2, 3, 2], [3, 4, 5], [4, 5, 1], [5, 0, 7], [0, 3, 12], [1, 4, 9], [2, 5, 8],
  [4, 1, 2], [3, 0, 6], [5, 2, 3]];

function byEveryPath(n, edges) {
  const out = Array.from({ length: n }, () => []);
  for (const [u, v, w] of edges) out[u].push([v, w]);
  const table = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 0 : null)));
  for (let s = 0; s < n; s++) {
    const stack = [[s, 0, new Set([s])]];
    while (stack.length) {
      const [u, total, seen] = stack.pop();
      for (const [v, w] of out[u]) {
        if (v === s && total + w < 0) return null;
        if (seen.has(v)) continue;
        if (table[s][v] === null || total + w < table[s][v]) table[s][v] = total + w;
        stack.push([v, total + w, new Set([...seen, v])]);
      }
    }
  }
  return table;
}

function seeded(seed) {
  return () => (seed = (seed * 48271) % 2147483647) / 2147483647;
}
const below = (random, k) => Math.floor(random() * k);
const between = (random, lo, hi) => lo + below(random, hi - lo + 1);
function shuffle(random, a) {
  for (let i = a.length - 1; i > 0; i--) {
    const j = below(random, i + 1);
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

function lineGraph(random, connected, isolated) {
  const n = connected + isolated;
  const labels = shuffle(random, Array.from({ length: n }, (_, i) => i));
  const onLine = labels.slice(0, connected);
  const spots = new Set();
  while (spots.size < connected) spots.add(below(random, 100000));
  const sorted = [...spots].sort((a, b) => a - b);
  const x = new Map(onLine.map((v, i) => [v, sorted[i]]));
  const raw = [];
  for (let i = 0; i + 1 < onLine.length; i++) {
    const [a, b] = [onLine[i], onLine[i + 1]];
    raw.push([a, b, x.get(b) - x.get(a)], [b, a, x.get(b) - x.get(a)]);
  }
  for (let k = 0; k < 1000; k++) {
    const a = onLine[below(random, onLine.length)];
    const b = onLine[below(random, onLine.length)];
    raw.push([a, b, Math.abs(x.get(a) - x.get(b)) + between(random, 0, 50)]);
  }
  const p = Array.from({ length: n }, () => between(random, -5000, 5000));
  const edges = raw.map(([u, v, w]) => [u, v, w + p[u] - p[v]]);
  const table = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 0 : null)));
  for (const a of onLine) for (const b of onLine) if (a !== b) table[a][b] = Math.abs(x.get(a) - x.get(b)) + p[a] - p[b];
  return { edges, table, onLine };
}

test("the lesson's six data centres", () => {
  assert.deepEqual(allPairs(6, DATA_CENTRES), [
    [0, 4, 7, 9, 13, 14], [11, 0, 3, 5, 9, 10], [8, 9, 0, 2, 7, 8],
    [6, 7, 9, 0, 5, 6], [8, 2, 4, 6, 0, 1], [7, 11, 3, 5, 10, 0],
  ]);
});

test('Q, R, S form a cycle of +1 with a negative edge in it; nothing leads back to P', () => {
  const edges = [[0, 1, 3], [1, 2, -2], [2, 3, 2], [3, 1, 1]];
  assert.deepEqual(allPairs(4, edges), [[0, 3, 1, 3], [null, 0, -2, 0], [null, 3, 0, 2], [null, 1, -1, 0]]);
});

test('S -> Q of -1 makes the cycle Q, R, S weigh -1', () => {
  assert.equal(allPairs(4, [[0, 1, 3], [1, 2, -2], [2, 3, 2], [3, 1, -1]]), null);
});

test('a loop of -1 on vertex 1 is a negative cycle, even though nothing else is wrong', () => {
  assert.equal(allPairs(3, [[0, 1, 2], [1, 2, 2], [1, 1, -1]]), null);
});

test('two edges from 0 to 1 (5 and 2) and a loop of +4 on vertex 1', () => {
  assert.deepEqual(allPairs(2, [[0, 1, 5], [0, 1, 2], [1, 1, 4], [1, 0, 1]]), [[0, 2], [1, 0]]);
});

test('300 small random graphs with weights from -3 to 9, against every path and every cycle', () => {
  const random = seeded(23);
  for (let k = 0; k < 300; k++) {
    const n = between(random, 1, 5);
    const m = below(random, 10);
    const edges = Array.from({ length: m }, () => [below(random, n), below(random, n), between(random, -3, 9)]);
    assert.deepEqual(allPairs(n, edges), byEveryPath(n, edges), JSON.stringify([n, edges]));
  }
});

test('120 vertices, about 1,200 edges, many of them negative, and 5 vertices with no edges at all', () => {
  const random = seeded(29);
  const { edges, table } = lineGraph(random, 115, 5);
  shuffle(random, edges);
  assert.deepEqual(allPairs(120, edges), table);
});

test('the same kind of graph, plus one edge that closes a cycle of weight -1', () => {
  const random = seeded(31);
  const { edges, table, onLine } = lineGraph(random, 115, 5);
  const [a, b] = [onLine[3], onLine[90]];
  edges.push([b, a, -table[a][b] - 1]);
  shuffle(random, edges);
  assert.equal(allPairs(120, edges), null);
});
A hint

Start the table with 0 on the diagonal, the cheapest direct edge where there is one and infinity elsewhere. Then let k run over all vertices in the outer loop and improve table[i][j] through k for every pair. At the end, a negative entry on the diagonal means a negative cycle; otherwise turn infinity into None.

The sample tests run on this device, in your browser (Pyodide, QuickJS): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Python Online Compiler Paste floyd_warshall.py, move the k loop inside the j loop, and compare the table with the one printed here.

Check yourself

4 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 4 In round k, dist[i][j] is 20, dist[i][k] is 7 and dist[k][j] is 9. What is dist[i][j] after the update?

    Type a number.

    Show the answer to question 1

    Answer: 16

    Going through k costs 7 + 9 = 16, which is below 20, so the entry becomes 16. Had it been 15 already, the path through k would not help and the entry would stay.

  2. Question 2 of 4 Which loop must be the outermost in Floyd-Warshall?

    Choose one answer.

    Show the answer to question 2

    Answer: The loop over k, the vertex newly allowed as a stop

    After round k, every entry is the best path that stops only at vertices 0 to k. That needs every pair updated for one k before the next k begins. With k inside, an entry is finished before the entries it depends on.

  3. Question 3 of 4 How many inner steps does Floyd-Warshall take for V = 500, in millions?

    Type a number.

    Show the answer to question 3

    Answer: 125 million

    The three loops each run V times: 500 × 500 × 500 = 125,000,000, that is 125 million steps, whatever the number of edges.

  4. Question 4 of 4 A road graph has 10,000 junctions, 40,000 roads and no negative weights. Which way of computing all distances is fastest?

    Choose one answer.

    Show the answer to question 4

    Answer: Dijkstra's algorithm from every junction

    Dijkstra from every vertex costs about V (V + E) log V ≈ 10⁴ × 5 × 10⁴ × 14 ≈ 7 × 10⁹ steps, against V³ = 10¹² for Floyd-Warshall and V² E = 4 × 10¹² for Bellman-Ford from every vertex. Breadth-first search would ignore the weights.

Why k must be the outer loop, and what negative cycles do to the numbers Pro: opens with a Pro, Premium or Ultimate pass

The loop order of Floyd-Warshall is not a detail: an invariant over k proves the algorithm, and the order that looks equivalent breaks it. This part proves the invariant, rebuilds routes from a next-hop table, measures the wrong order on random graphs, and reproduces the exponentially large numbers that a negative cycle creates.

In this part:

  • The invariant over k, proved
  • Routes from a next-hop table, and the wrong loop order, measured
  • Exponentially large entries on a negative cycle, reproduced

2 runnable examples (Python) · about 2 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Reachability, Johnson's reweighting and the size limit, measured Pro: opens with a Pro, Premium or Ultimate pass

The same triple loop answers reachability, Johnson’s reweighting beats it on sparse graphs, and Python itself sets a practical limit on V. This part computes a dependency closure with bitsets, works Johnson’s algorithm through on five vertices, times Floyd-Warshall up to V = 500, and compares the three methods with numbers.

In this part:

  • Warshall’s transitive closure on a package graph, with bitsets
  • Johnson’s reweighting, worked on five vertices
  • How large V can be in Python, timed

3 runnable examples (Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Floyd-Warshall in five languages, and a practice set Pro: opens with a Pro, Premium or Ultimate pass

The triple loop is the same in every language, but infinity is not, and that is where implementations go wrong. This part runs the data-centre table in Python, JavaScript, Java, C++ and Go with recorded output, explains the guard that keeps infinity from overflowing, and ends with a practice set with answers.

In this part:

  • Floyd-Warshall in Python, JavaScript, Java, C++ and Go
  • The sentinel and the 32-bit trap, with numbers
  • Practice set

2 runnable examples (Build and run all three, C++, Go, Java, JavaScript, Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher to mid · Warm-up

    When is Floyd-Warshall a better choice than running Dijkstra's algorithm from every vertex?

    Floyd-Warshall costs V³ steps whatever the number of edges, while Dijkstra's algorithm from every vertex costs about V (V + E) log V. On a dense graph, where E is close to V², the second is about V³ log V, so Floyd-Warshall wins, and its three short loops have small constant factors. It also accepts negative edge weights and shows a negative cycle on its diagonal, which repeated Dijkstra cannot do. Dijkstra from every vertex wins on sparse graphs: with V = 10⁴ and E = 5 × 10⁴ it needs about 7 × 10⁹ steps against 10¹² for Floyd-Warshall, and it never has to hold the whole V × V table at once. On a sparse graph with negative weights, Johnson's algorithm reweights the edges so that the Dijkstra runs become possible.

  2. Mid to senior

    A logistics company has 300 hubs and 6,000 one-way lanes with costs; some lanes carry a credit for return loads, so their cost is negative. Every night it needs the cheapest cost between every pair of hubs. Which method do you use, how long will it take, and how do you catch bad data?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Senior to staff

    You keep an all-pairs table of travel times for 2,000 junctions. A new one-way road opens from u to v with travel time w. How do you update the table without running Floyd-Warshall again, what does it cost, and what changes if a road closes instead?

    Model answer, Pro. First step and follow-ups

  4. Senior to staff

    A catalogue has 20,000 courses and 60,000 prerequisite links, and the registrar wants to answer "does course A depend on course B, directly or not?" instantly for any pair. Compare Warshall's closure with booleans, with bitsets, and a search from every course, in memory and time.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Floyd-Warshall fills the table of all distances with a triple loop in O(V³) time and O(V²) memory.
  • The vertex k that may be used as a stop is the outer loop; after round k, paths may stop at vertices 0 to k.
  • Negative edges are fine, and a negative entry on the diagonal means a negative cycle.
  • On sparse graphs, Dijkstra’s algorithm from every vertex, or Johnson’s algorithm with negative edges, is faster.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.