CO2 Topic 14 of 34
Bellman–Ford
Single-source shortest paths when edges may be negative: relax every edge V − 1 times, then one more pass reveals any negative-weight cycle.
You will be able to
- Explain why V − 1 rounds of relaxing every edge are enough
- Trace Bellman–Ford pass by pass in a given edge order
- Detect a negative-weight cycle reachable from the source
- Choose between Dijkstra and Bellman–Ford for a problem
Read
- Textbook 1, §24.1 The Bellman-Ford algorithm
- Textbook 2, §5.4 Single-source shortest paths: general weights
- Reference book 1, §6.3 (shortest paths)
Watch
Intuition
Dijkstra's greedy choice breaks as soon as an edge can be negative (topic 13). Bellman–Ford gives up being clever: it simply relaxes every edge, over and over.
Why does that work? A shortest path never repeats a vertex (if there is no negative cycle), so it has at most V − 1 edges. After round 1, every shortest path that uses 1 edge is correct; after round 2, every one using 2 edges; … after round V − 1, all of them.
If a V-th round still improves some distance, the only explanation is a negative-weight cycle — going round it again and again makes paths shorter forever, so shortest paths are undefined.
digraph {
node [shape=circle, fontname="Inter"]; edge [fontname="JetBrains Mono"];
S [style=filled, fillcolor="#fbbf24"];
S -> A [label="6"]; S -> B [label="7"]; A -> C [label="5"]; A -> D [label="-4"]; A -> B [label="8"];
B -> C [label="-3"]; B -> D [label="9"]; C -> A [label="-2"]; D -> S [label="2"]; D -> C [label="7"];
}
How it works
Edge order used in every pass: S→A, S→B, A→B, A→C, A→D, B→C, B→D, C→A, D→S, D→C.
| After pass | S | A | B | C | D |
|---|---|---|---|---|---|
| start | 0 | ∞ | ∞ | ∞ | ∞ |
| 1 | 0 | 2 | 7 | 4 | 2 |
| 2 | 0 | 2 | 7 | 4 | −2 |
| 3 | 0 | 2 | 7 | 4 | −2 |
Pass 1 in detail — later edges in the same pass already see values updated earlier in that pass:
- S→A: A = 6; S→B: B = 7; A→B: 14, no change; A→C: C = 11; A→D: D = 2;
- B→C: C = 7 − 3 = 4; B→D: 16, no change; C→A: A = 4 − 2 = 2; D→S, D→C: no change.
Pass 2: A→D now gives D = 2 − 4 = −2. Pass 3 changes nothing, so the algorithm can stop early, and the final checking pass finds no improvement: no negative cycle.
With a less lucky edge order the same values take more passes (try the reverse order in problem 1) — but never more than V − 1 = 4.
Final distances: A 2, B 7, C 4, D −2 (path to D: S → B → C → A → D = 7 − 3 − 2 − 4).
Python implementation
def bellman_ford(vertices, edges, source):
"""edges: list of (u, v, w). Returns (dist, parent), or raises ValueError on a negative cycle."""
dist = {v: float("inf") for v in vertices}
parent = {v: None for v in vertices}
dist[source] = 0
for _ in range(len(vertices) - 1):
changed = False
for u, v, w in edges:
if dist[u] + w < dist[v]:
dist[v], parent[v] = dist[u] + w, u
changed = True
if not changed: # early exit: already optimal
break
for u, v, w in edges: # one more pass: still improving?
if dist[u] + w < dist[v]:
raise ValueError("negative-weight cycle reachable from the source")
return dist, parent
if __name__ == "__main__":
V = ["S", "A", "B", "C", "D"]
E = [("S", "A", 6), ("S", "B", 7), ("A", "B", 8), ("A", "C", 5), ("A", "D", -4),
("B", "C", -3), ("B", "D", 9), ("C", "A", -2), ("D", "S", 2), ("D", "C", 7)]
print(bellman_ford(V, E, "S")[0]) # {'S': 0, 'A': 2, 'B': 7, 'C': 4, 'D': -2}
try:
bellman_ford(["X", "Y", "Z"], [("X", "Y", 1), ("Y", "Z", -2), ("Z", "Y", 1)], "X")
except ValueError as e:
print(e) # negative-weight cycle reachable from the source
Complexity
| Bellman–Ford | Dijkstra (binary heap) | |
|---|---|---|
| time | O(V·E) | O((V + E) log V) |
| negative edges | ✓ allowed | ✗ |
| negative-cycle detection | ✓ | ✗ |
Bellman–Ford is slower, but it is the right tool when weights can be negative — for example, currency exchange (log of rates), or distance-vector routing protocols (RIP), where each router only knows its neighbours.
Common mistakes
- Doing only one pass. You need up to V − 1 passes; one pass only fixes paths whose edges happen to appear in the right order.
- Skipping the final check — then a negative cycle silently produces garbage distances.
- Thinking any negative edge is a problem. Negative edges are fine; only negative cycles make shortest paths undefined.
- Undirected graphs with a negative edge. An undirected negative edge u–v is itself a negative cycle u→v→u.
Problems
- Re-run the trace with the edge order reversed. Do you get the same final distances? The same number of passes?
- Lab: detect arbitrage: given exchange rates between currencies, decide whether a sequence of trades can turn 1 unit into more than 1. (Hint: weights −log(rate).)
- Lab: after detecting a negative cycle, print the vertices on it. (Hint: follow parent pointers V times from a vertex that was still relaxed.)
- Why can't we just add a large constant to every edge to make the weights non-negative and then use Dijkstra?
- In a DAG with negative edges, shortest paths can be found in O(V + E). How? (Hint: topological order.)
Quiz
GATE-style practice
Questions in the GATE CSE formats: MCQ (one correct option), MSQ (one or more correct options) and NAT (type a number). Each comes with a full worked solution. These are original practice questions, not past papers; for the real thing, see the official GATE site and its previous question papers.