Foundations

Dijkstra vs Bellman-Ford vs Floyd-Warshall

Dijkstra, Bellman-Ford and Floyd-Warshall all find shortest paths, but they answer different questions under different conditions. This guide runs all three on one example graph with a negative edge, shows exactly why Dijkstra fails on it, explains how the other two detect negative cycles, compares their speed with measured operation counts, and covers Johnson's algorithm and the special cases where a simpler method wins, with tested Python code throughout.

22 Min Read Updated: October 2026 Beginner Level
Mohammed Islam Hadjoudj
Mohammed Islam Hadjoudj
Expert Operations Research Engineer

1. The short answer

Dijkstra's algorithm, the Bellman-Ford algorithm and the Floyd-Warshall algorithm all find shortest paths in a weighted graph, but they answer different questions under different conditions. Two questions decide between them: do you need distances from one source or between every pair of vertices, and can an edge have a negative weight?

DijkstraBellman-FordFloyd-Warshall
AnswersOne source to every vertexOne source to every vertexEvery pair of vertices
Negative edge weightsNo: gives wrong answersYesYes
Detects negative cyclesNoYesYes
TimeO((V + E) log V) with a binary heapO(V E)O(V3)
MemoryO(V)O(V)O(V2)
StrategyGreedy: settle the closest vertexRepeat: relax every edgeDynamic programming over stopovers
Use it whenWeights are nonnegative, which covers most road, network and game mapsSome weights are negative, or a negative cycle must be detectedYou need all distances in a small or dense graph

Here V is the number of vertices and E the number of edges. In one sentence: use Dijkstra by default, switch to Bellman-Ford when weights can be negative, and use Floyd-Warshall when you need the whole distance table of a graph with at most a few hundred vertices. When you need all pairs in a large sparse graph with negative edges, Johnson's algorithm in section 7 combines the first two. The rest of this guide runs all three on the same graph and shows where each one wins and where each one breaks.

A decision chart. First question: do you need all pairs or one source? For one source: can weights be negative? If no, Dijkstra; if yes, Bellman-Ford. For all pairs: can weights be negative and is the graph dense? Dense or small graphs use Floyd-Warshall; sparse graphs use Dijkstra from every vertex when weights are nonnegative and Johnson's algorithm when they are not. Side notes: unweighted graphs need only breadth-first search, and acyclic graphs can be solved in linear time in topological order.
Two questions pick the algorithm: one source or all pairs, and whether negative weights can occur. Density decides between the all-pairs options.

2. One graph, three answers

The running example is a small directed graph with six vertices, seven edges and one negative edge, B → A with weight -4. Think of the weights as costs: a negative cost is a refund, such as a toll road that pays you to take it, or a step in a chemical process that releases energy.

S → A  3      S → B  6      A → C  4      B → A  -4
C → T  2      B → D  3      D → T  4

6 vertices, 7 edges, no cycles; source S

The cheapest route from S to T is S, B, A, C, T, with cost 6 - 4 + 4 + 2 = 8. It starts with the more expensive first step, to B, and earns it back on the refund. The program below implements the three algorithms in their standard textbook form and runs them from S.

import heapq
INF = float('inf')

def graph(edges):
    adj = {}
    for u, v, w in edges:
        adj.setdefault(u, []).append((v, w))
        adj.setdefault(v, [])
    return adj

def dijkstra(adj, s):
    """Greedy: settle the closest unsettled vertex, never revisit it."""
    dist = {v: INF for v in adj}
    dist[s] = 0
    done, heap = set(), [(0, s)]
    while heap:
        d, u = heapq.heappop(heap)
        if u in done:
            continue                      # stale heap entry
        done.add(u)
        for v, w in adj[u]:
            if v not in done and d + w < dist[v]:
                dist[v] = d + w
                heapq.heappush(heap, (dist[v], v))
    return dist

def bellman_ford(adj, s):
    """Relax every edge, round after round, until nothing changes."""
    dist = {v: INF for v in adj}
    dist[s] = 0
    for _ in range(len(adj) - 1):
        changed = False
        for u in adj:
            for v, w in adj[u]:
                if dist[u] + w < dist[v]:
                    dist[v] = dist[u] + w
                    changed = True
        if not changed:
            break
    return dist

def floyd_warshall(adj):
    """All pairs: allow the vertices as stopovers one at a time."""
    d = {u: {v: 0 if u == v else INF for v in adj} for u in adj}
    for u in adj:
        for v, w in adj[u]:
            d[u][v] = min(d[u][v], w)
    for k in adj:
        for i in adj:
            for j in adj:
                if d[i][k] + d[k][j] < d[i][j]:
                    d[i][j] = d[i][k] + d[k][j]
    return d

G = graph([('S', 'A', 3), ('S', 'B', 6), ('A', 'C', 4), ('B', 'A', -4),
           ('C', 'T', 2), ('B', 'D', 3), ('D', 'T', 4)])
dj, bf, fw = dijkstra(G, 'S'), bellman_ford(G, 'S'), floyd_warshall(G)['S']
for v in G:
    print(v, dj[v], bf[v], fw[v], '' if dj[v] == bf[v] else '<- Dijkstra is wrong')
# S 0 0 0
# A 3 2 2 <- Dijkstra is wrong
# B 6 6 6
# C 7 6 6 <- Dijkstra is wrong
# T 9 8 8 <- Dijkstra is wrong
# D 9 9 9
Left: the example graph with vertices S, A, B, C, D and T, the negative edge B to A drawn in red, and the shortest route S, B, A, C, T in green. Right: a table of the distances from S. The true distances are S 0, A 2, B 6, C 6, D 9 and T 8. Bellman-Ford and Floyd-Warshall match them all. Dijkstra reports A 3, C 7 and T 9, three wrong values shown in red.
On a graph with one negative edge, Dijkstra gets three of six distances wrong. Bellman-Ford and Floyd-Warshall agree with the true values.

Bellman-Ford and Floyd-Warshall return the true distances. Dijkstra reports 3 for A, 7 for C and 9 for T, all too high, and it raises no error: the wrong answers look exactly like right ones. Section 4 explains why. On a graph with nonnegative weights all three would agree, and the only difference would be speed.

3. What each algorithm does

All three algorithms are built from one operation, relaxation. Each vertex v carries a tentative distance dist[v], which starts at infinity, or 0 for the source. Relaxing the edge u → v with weight w asks whether going through u is cheaper, and if so records it:

if dist[u] + w < dist[v]:
    dist[v] = dist[u] + w

A tentative distance only ever goes down, and it is always the cost of some real route. The algorithms differ only in which edges they relax and in what order.

Left: Dijkstra's run on the example as a timeline. It settles S at 0, then A at 3, which is declared final; when B is settled at 6, the cheaper route B to A of cost 2 arrives too late, so C is settled at 7 and T at 9. Right: Bellman-Ford's rounds as a table. Round 1 gives A 2, B 6, C 7, D 9 and T 9; round 2 lowers C to 6 and T to 8; round 3 changes nothing, so the algorithm stops.
Dijkstra commits to A at cost 3 before it has seen the refund. Bellman-Ford keeps relaxing every edge until a whole round changes nothing.

On the example, Bellman-Ford visits the edges in the order they were listed. Its first round already lowers A to 2 when it reaches the edges out of B, but by then it has used the old value 3 to set C = 7. The second round carries the improvement on to C = 6 and T = 8. The third round changes nothing, so the algorithm stops early instead of running all V - 1 = 5 rounds. How many rounds are needed depends on the edge order, but never exceeds V - 1.

4. Why Dijkstra breaks on negative edges

Dijkstra's speed comes from one promise: when a vertex leaves the priority queue, its distance is final. The promise rests on an argument that needs nonnegative weights. Suppose u is the closest unsettled vertex. Any other route to u has to leave the settled region through some unsettled vertex x, which is already at least as far as u, and the rest of the route can only add cost. With a negative edge, the rest of the route can subtract cost, and the argument collapses.

That is exactly what happens in the example. Dijkstra settles S at 0, then A at 3, because 3 is less than B's 6. When B is settled and its edge to A offers 6 - 4 = 2, A is already final and the improvement is thrown away. Everything built on top of A inherits the error: C is settled at 3 + 4 = 7 instead of 6, and T at 9 instead of 8.

Two tempting fixes do not work:

With nonnegative weights, none of this arises, and Dijkstra is correct and the fastest of the three. Zero weights are fine: the argument only needs that no edge decreases the cost.

5. Negative cycles

A negative cycle is a cycle whose weights add up to less than zero. If one can be reached from the source and can reach the target, there is no shortest route at all: going round the cycle once more is always cheaper. The distance is minus infinity, and an algorithm should report the cycle rather than a number. Add the edge C → B with weight -5 to the example and the cycle B, A, C, B costs -4 + 4 - 5 = -5.

INF = float('inf')

def graph(edges):
    adj = {}
    for u, v, w in edges:
        adj.setdefault(u, []).append((v, w))
        adj.setdefault(v, [])
    return adj

def negative_cycle(adj):
    """Bellman-Ford from a virtual source joined to every vertex at cost 0.
    Returns a negative cycle as a list of vertices, or None."""
    dist = {v: 0 for v in adj}
    parent = {v: None for v in adj}
    for _ in range(len(adj)):                  # one round more than usual
        last = None
        for u in adj:
            for v, w in adj[u]:
                if dist[u] + w < dist[v]:
                    dist[v], parent[v], last = dist[u] + w, u, v
        if last is None:
            return None                        # a quiet round: no negative cycle
    for _ in range(len(adj)):                  # step back far enough to land on the cycle
        last = parent[last]
    cycle, v = [last], parent[last]
    while v != last:                           # follow parents once round the cycle
        cycle.append(v)
        v = parent[v]
    cycle.reverse()                            # parents point backwards
    return cycle + cycle[:1]

def floyd_warshall_diagonal(adj):
    """Vertices whose distance to themselves comes out negative."""
    d = {u: {v: 0 if u == v else INF for v in adj} for u in adj}
    for u in adj:
        for v, w in adj[u]:
            d[u][v] = min(d[u][v], w)
    for k in adj:
        for i in adj:
            for j in adj:
                if d[i][k] + d[k][j] < d[i][j]:
                    d[i][j] = d[i][k] + d[k][j]
    return [v for v in adj if d[v][v] < 0]

edges = [('S', 'A', 3), ('S', 'B', 6), ('A', 'C', 4), ('B', 'A', -4),
         ('C', 'T', 2), ('B', 'D', 3), ('D', 'T', 4)]
print(negative_cycle(graph(edges)))
# None
bad = graph(edges + [('C', 'B', -5)])          # B -> A -> C -> B costs -4 + 4 - 5 = -5
print(negative_cycle(bad))
# ['C', 'B', 'A', 'C']
print(floyd_warshall_diagonal(bad))
# ['A', 'B', 'C']

The function starts Bellman-Ford from a virtual source joined to every vertex at cost 0, so it finds a negative cycle anywhere in the graph, not only one reachable from S. On the original example a round eventually changes nothing and it returns None. With the extra edge it returns the cycle C, B, A, C, the same cycle written from a different start, and Floyd-Warshall flags A, B and C on its diagonal. In testing on thousands of random graphs, the diagonal flagged a negative cycle exactly when one existed, but did not always flag every vertex that can reach one, so use it as a yes-or-no test.

One trap catches many people. In an undirected graph, an edge can be walked both ways, so a single edge with negative weight is already a negative cycle: u, v, u. Bellman-Ford run on the directed version of an undirected graph will therefore report a negative cycle whenever any edge is negative, a pitfall that Sedgewick and Wayne's Algorithms spells out. Shortest simple paths with negative weights in undirected graphs need different, more involved methods.

6. Speed: which one is faster, and when

For a single source the order is clear whenever Dijkstra applies: O((V + E) log V) with a binary heap against Bellman-Ford's O(V E). On a graph with 10,000 vertices and 50,000 edges, that is roughly a million heap steps against up to 500 million edge relaxations. Floyd-Warshall is not a single-source method, and running it for one source wastes almost all its work.

For all pairs the comparison is closer, because the alternatives are to run a single-source algorithm from every vertex:

All-pairs methodTimeNegative edgesBest for
Dijkstra from every vertexO(V (V + E) log V)NoSparse graphs, nonnegative weights
Bellman-Ford from every vertexO(V2 E)YesAlmost never
Floyd-WarshallO(V3)YesDense or small graphs
Johnson's algorithmO(V E + V (V + E) log V)YesSparse graphs with negative edges

Asymptotic bounds hide constants, so the program below counts the basic steps each method actually performs on two random directed graphs with 100 vertices: a sparse one with 300 edges and a dense one with 4,950, half of all possible edges. For Dijkstra it counts edge checks and heap operations separately, since a heap operation costs about log V steps.

import heapq, random
INF = float('inf')

def random_graph(n, m, seed):
    """n vertices, m distinct directed edges with weights 1..100."""
    rnd = random.Random(seed)
    pairs = rnd.sample([(u, v) for u in range(n) for v in range(n) if u != v], m)
    adj = {v: [] for v in range(n)}
    for u, v in pairs:
        adj[u].append((v, rnd.randint(1, 100)))
    return adj

def all_pairs_dijkstra(adj):
    """Dijkstra from every vertex. Counts edge checks and heap operations."""
    checks = heap_ops = 0
    for s in adj:
        dist, heap = {v: INF for v in adj}, [(0, s)]
        dist[s] = 0
        while heap:
            d, u = heapq.heappop(heap)
            heap_ops += 1
            if d > dist[u]:
                continue
            for v, w in adj[u]:
                checks += 1
                if d + w < dist[v]:
                    dist[v] = d + w
                    heapq.heappush(heap, (dist[v], v))
                    heap_ops += 1
    return checks, heap_ops

def all_pairs_bellman_ford(adj):
    """Bellman-Ford from every vertex, stopping early when a round changes nothing."""
    m, checks = sum(len(a) for a in adj.values()), 0
    for s in adj:
        dist = {v: INF for v in adj}
        dist[s] = 0
        for _ in range(len(adj) - 1):
            changed = False
            for u in adj:
                for v, w in adj[u]:
                    if dist[u] + w < dist[v]:
                        dist[v], changed = dist[u] + w, True
            checks += m
            if not changed:
                break
    return checks

n = 100
for name, m in [('sparse', 3 * n), ('dense', n * (n - 1) // 2)]:
    adj = random_graph(n, m, seed=1)
    checks, heap_ops = all_pairs_dijkstra(adj)
    print(f'{name:6} E={m:4}  Dijkstra x V: {checks:7,} checks + {heap_ops:6,} heap ops   '
          f'Bellman-Ford x V: {all_pairs_bellman_ford(adj):9,}   Floyd-Warshall: {n ** 3:,}')
# sparse E= 300  Dijkstra x V:  27,565 checks + 21,388 heap ops   Bellman-Ford x V:   226,200   Floyd-Warshall: 1,000,000
# dense  E=4950  Dijkstra x V: 495,000 checks + 64,770 heap ops   Bellman-Ford x V: 2,712,600   Floyd-Warshall: 1,000,000
A bar chart on a logarithmic scale of the steps needed to compute all-pairs distances on random graphs with 100 vertices. On the sparse graph with 300 edges: Dijkstra from every vertex about 49,000 steps, Bellman-Ford from every vertex 226,200, Floyd-Warshall 1,000,000. On the dense graph with 4,950 edges: Dijkstra from every vertex about 560,000, Bellman-Ford 2,712,600, Floyd-Warshall 1,000,000.
All-pairs work on 100 vertices. On the sparse graph Dijkstra from every vertex is about 20 times cheaper than Floyd-Warshall; on the dense graph the two are within a factor of two.

On the sparse graph, Dijkstra from every vertex needs about 49,000 steps against Floyd-Warshall's fixed 1,000,000, a factor of about 20. On the dense graph the gap almost closes, and once the heap's logarithmic cost per operation is counted, Floyd-Warshall's simple inner loop, which touches memory in order and needs no priority queue, often wins in practice. Bellman-Ford from every vertex is the slowest in both cases, even with early stopping. The rule of thumb: below a few hundred vertices, or when most pairs of vertices are joined by an edge, use Floyd-Warshall for its simplicity; on large sparse graphs, use Dijkstra or Johnson from every vertex.

7. All pairs with negative edges: Johnson's algorithm

Donald Johnson's algorithm of 1977 gets the best of both. It runs Bellman-Ford once to compute a number h(v) for every vertex, the distance from a virtual source joined to all vertices at cost 0. It then replaces each weight by

w'(u, v) = w(u, v) + h(u) - h(v)

Because h satisfies h(v) ≤ h(u) + w(u, v) for every edge, every new weight is nonnegative. And along any route from s to t the terms telescope, so every route's cost changes by the same amount, h(s) - h(t). Shortest routes stay shortest, and Dijkstra can now be run from every vertex. Unlike adding a constant, this reweighting does not depend on how many edges a route has.

import heapq
INF = float('inf')

def graph(edges):
    adj = {}
    for u, v, w in edges:
        adj.setdefault(u, []).append((v, w))
        adj.setdefault(v, [])
    return adj

def dijkstra(adj, s):
    dist, heap = {v: INF for v in adj}, [(0, s)]
    dist[s] = 0
    while heap:
        d, u = heapq.heappop(heap)
        if d > dist[u]:
            continue
        for v, w in adj[u]:
            if d + w < dist[v]:
                dist[v] = d + w
                heapq.heappush(heap, (dist[v], v))
    return dist

def johnson(adj):
    """All pairs with negative edges: one Bellman-Ford, then Dijkstra from every vertex."""
    h = {v: 0 for v in adj}                    # distances from a virtual source
    for _ in range(len(adj)):
        for u in adj:
            for v, w in adj[u]:
                h[v] = min(h[v], h[u] + w)
    for u in adj:
        for v, w in adj[u]:
            if h[u] + w < h[v]:
                raise ValueError('negative cycle')
    # reweight: every edge becomes nonnegative, every path changes by h[s] - h[t]
    lifted = {u: [(v, w + h[u] - h[v]) for v, w in adj[u]] for u in adj}
    return {s: {t: d - h[s] + h[t] for t, d in dijkstra(lifted, s).items()} for s in adj}, lifted

G = graph([('S', 'A', 3), ('S', 'B', 6), ('A', 'C', 4), ('B', 'A', -4),
           ('C', 'T', 2), ('B', 'D', 3), ('D', 'T', 4)])
dist, lifted = johnson(G)
print({u: [(v, w) for v, w in lifted[u]] for u in ('S', 'A', 'B')})
# {'S': [('A', 7), ('B', 6)], 'A': [('C', 0)], 'B': [('A', 0), ('D', 3)]}
print(dist['S'])
# {'S': 0, 'A': 2, 'B': 6, 'C': 6, 'T': 8, 'D': 9}
print(dist['B'])
# {'S': inf, 'A': -4, 'B': 0, 'C': 0, 'T': 2, 'D': 3}

In the example, h(A) = -4 and every other vertex gets 0. The negative edge B → A becomes 0, S → A rises from 3 to 7 and A → C falls from 4 to 0, and Dijkstra on the new weights, corrected back by h, gives the true distances from every source, including the row for B with its negative entry -4. If Bellman-Ford finds a negative cycle in the first step, Johnson's algorithm stops and reports it.

8. When none of the three is the best choice

Some graphs have structure that a simpler or faster method can exploit:

9. Memory and recovering the paths

Distances are often not enough: you want the route itself. Dijkstra and Bellman-Ford store one parent per vertex, the vertex the best route arrived from, and the route to t is read backwards from t. That costs O(V) extra memory. Floyd-Warshall needs a full V by V table of "next hop" entries alongside its distance table, so its memory is O(V2). For 10,000 vertices with 8-byte entries, the distance table alone takes about 800 megabytes, and the next-hop table adds as much again. Memory, more than time, is usually what rules Floyd-Warshall out on large graphs.

10. Where each one is used

11. Still an active research area

Shortest paths are one of the oldest problems in algorithms, and the bounds are still moving. In 2022, Aaron Bernstein, Danupon Nanongkai and Christian Wulff-Nilsen showed that single-source shortest paths with negative integer weights can be solved in nearly linear time, far below Bellman-Ford's O(V E). In 2024, Bernhard Haeupler and coauthors proved that Dijkstra's algorithm with a suitably designed heap is universally optimal for ordering vertices by distance: on every fixed graph, its worst case over all edge weights is within a constant factor of the best any algorithm can do. And in 2025, Ran Duan and coauthors gave a deterministic algorithm for directed graphs with nonnegative weights that runs in O(E log2/3 V) time, beating Dijkstra's O(E + V log V) on sparse graphs. It escapes what was long called the sorting barrier because, unlike Dijkstra, it does not produce the vertices in order of distance. Each of the three papers won a best paper award. None of these has yet replaced the three classics in everyday code, where simplicity and small constants matter more.

12. Common mistakes

MistakeWhy it is wrongFix
Running Dijkstra on a graph with negative edgesSettled vertices are never revisited; the example gets A, C and T wrongUse Bellman-Ford, or Johnson for all pairs
Adding a constant to make weights nonnegativeIt penalises routes with more edges and changes which route is shortestReweight with potentials, as Johnson's algorithm does
Ignoring negative cyclesDistances are minus infinity; any number reported is meaninglessRun one extra Bellman-Ford round, or check the Floyd-Warshall diagonal
A negative edge in an undirected graphWalking it there and back is a negative cycleTreat the problem as shortest simple paths, which needs other methods
Using Floyd-Warshall for one sourceO(V3) work for one row of the tableUse Dijkstra or Bellman-Ford
Using Floyd-Warshall on a huge sparse graphThe V by V table may not fit in memoryRun Dijkstra or Johnson from the sources you need
Using INF + w with a fixed large integerInfinity plus a negative weight looks like a real distance, or overflowsSkip relaxations from vertices still at infinity
Running Bellman-Ford for exactly V rounds and trusting the resultRound V is the test round; changes there mean a negative cycleRun V - 1 rounds, then check once more

13. Glossary

TermMeaning
Single-source shortest pathsDistances from one vertex to every other vertex
All-pairs shortest pathsDistances between every ordered pair of vertices
RelaxationChecking whether a route through one more edge is cheaper, and recording it if so
Settled vertexIn Dijkstra's algorithm, a vertex whose distance has been declared final
Negative edgeAn edge with a weight below zero
Negative cycleA cycle whose weights add up to less than zero
Priority queueA structure that always returns the smallest key; usually a binary heap
PotentialA number h(v) per vertex used to reweight edges without changing which routes are shortest
Dense graphA graph in which a large fraction of all vertex pairs are joined by edges
Sparse graphA graph with few edges compared with V2, typically a small multiple of V
Transitive closureThe table of which vertices can reach which

14. Frequently asked questions

What is the difference between Dijkstra, Bellman-Ford and Floyd-Warshall?

Dijkstra and Bellman-Ford compute distances from one source vertex to every other vertex, while Floyd-Warshall computes the distance between every pair of vertices. Dijkstra is the fastest but requires nonnegative edge weights. Bellman-Ford handles negative weights and detects negative cycles in O(VE) time. Floyd-Warshall also handles negative weights and detects negative cycles, in O(V cubed) time and O(V squared) memory.

Why does Dijkstra not work with negative weights?

Dijkstra declares a vertex's distance final as soon as it is the closest unsettled vertex, and never revisits it. That is only safe if no later edge can lower the cost. A negative edge can do exactly that, so Dijkstra may finalise a vertex too early and report distances that are too high, without any warning.

Is Bellman-Ford slower than Dijkstra?

Yes, whenever both apply. Bellman-Ford runs in O(VE) time, while Dijkstra with a binary heap runs in O((V + E) log V). On a graph with 10,000 vertices and 50,000 edges that is up to 500 million edge relaxations against about a million heap steps. Use Bellman-Ford only when weights can be negative or a negative cycle must be detected.

When should I use Floyd-Warshall instead of running Dijkstra from every vertex?

Use Floyd-Warshall when you need all pairs of distances in a small graph, up to a few hundred vertices, or in a dense graph in which most pairs of vertices are joined by edges, and also when weights can be negative. On large sparse graphs, running Dijkstra from every vertex, or Johnson's algorithm if weights can be negative, is much faster and uses far less memory.

How do you detect a negative cycle?

Run Bellman-Ford for V - 1 rounds and then one more: if the extra round still lowers any distance, a negative cycle exists, and following parent pointers from the changed vertex leads onto it. With Floyd-Warshall, a negative cycle exists exactly when some entry on the diagonal of the final distance matrix is negative. Dijkstra cannot detect negative cycles.

What is Johnson's algorithm?

Johnson's algorithm computes all-pairs shortest paths in sparse graphs with negative edges. It runs Bellman-Ford once to get a potential h(v) for every vertex, reweights each edge to w(u, v) + h(u) - h(v), which is never negative and keeps the same shortest paths, and then runs Dijkstra from every vertex. Its running time is O(VE + V(V + E) log V) with a binary heap.

15. References

The algorithms, bounds and attributions above come from these sources, listed in chronological order.

  1. Shimbel, A. (1955). "Structure in Communication Nets." In Proceedings of the Symposium on Information Networks, 199 to 203. Brooklyn: Polytechnic Press of the Polytechnic Institute of Brooklyn.
  2. Ford, L. R. (1956). Network Flow Theory. Paper P-923. Santa Monica: RAND Corporation.
  3. Bellman, R. (1958). "On a Routing Problem." Quarterly of Applied Mathematics 16(1), 87 to 90.
  4. Dijkstra, E. W. (1959). "A Note on Two Problems in Connexion with Graphs." Numerische Mathematik 1, 269 to 271.
  5. Moore, E. F. (1959). "The Shortest Path Through a Maze." In Proceedings of an International Symposium on the Theory of Switching, 285 to 292. Cambridge, MA: Harvard University Press.
  6. Roy, B. (1959). "Transitivité et connexité." Comptes Rendus de l'Académie des Sciences 249, 216 to 218.
  7. Floyd, R. W. (1962). "Algorithm 97: Shortest Path." Communications of the ACM 5(6), 345.
  8. Warshall, S. (1962). "A Theorem on Boolean Matrices." Journal of the ACM 9(1), 11 to 12.
  9. Johnson, D. B. (1973). "A Note on Dijkstra's Shortest Path Algorithm." Journal of the ACM 20(3), 385 to 388.
  10. Johnson, D. B. (1977). "Efficient Algorithms for Shortest Paths in Sparse Networks." Journal of the ACM 24(1), 1 to 13.
  11. Fredman, M. L. and Tarjan, R. E. (1987). "Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms." Journal of the ACM 34(3), 596 to 615.
  12. Moy, J. (1998). "OSPF Version 2." RFC 2328. Internet Engineering Task Force.
  13. Malkin, G. (1998). "RIP Version 2." RFC 2453. Internet Engineering Task Force.
  14. Cormen, T. H., Leiserson, C. E., Rivest, R. L. and Stein, C. (2009). Introduction to Algorithms, 3rd edition. Cambridge, MA: MIT Press.
  15. Frana, P. L. and Misa, T. J. (2010). "An Interview with Edsger W. Dijkstra." Communications of the ACM 53(8), 41 to 47.
  16. Hougardy, S. (2010). "The Floyd-Warshall Algorithm on Graphs with Negative Cycles." Information Processing Letters 110(8 to 9), 279 to 281.
  17. Sedgewick, R. and Wayne, K. (2011). Algorithms, 4th edition. Upper Saddle River, NJ: Addison-Wesley.
  18. Schrijver, A. (2012). "On the History of the Shortest Path Problem." Documenta Mathematica, Extra Volume ISMP, 155 to 167.
  19. Bernstein, A., Nanongkai, D. and Wulff-Nilsen, C. (2022). "Negative-Weight Single-Source Shortest Paths in Near-linear Time." Proceedings of the 63rd IEEE Symposium on Foundations of Computer Science, 600 to 611.
  20. Haeupler, B., Hladík, R., Rozhoň, V., Tarjan, R. E. and Tětek, J. (2024). "Universal Optimality of Dijkstra via Beyond-Worst-Case Heaps." Proceedings of the 65th IEEE Symposium on Foundations of Computer Science, 2099 to 2130.
  21. Duan, R., Mao, J., Mao, X., Shu, X. and Yin, L. (2025). "Breaking the Sorting Barrier for Directed Single-Source Shortest Paths." Proceedings of the 57th ACM Symposium on Theory of Computing, 36 to 44.

Watch the negative edge do its work

Build the example in the visualizer, set the edge from B to A to -4 and run Bellman-Ford from S. Watch the refund reach A and then pass on to C and T.

Open the visualizer

Race the Three Algorithms

Run Dijkstra step by step on your own graph and watch which vertex it settles next. Then make one weight negative and compare it with Bellman-Ford.

Launch the Dijkstra Visualizer