The problem, relaxation, and Bellman-Ford
Lucas P. Cordova, Ph.D.
Willamette University | Fall 2026
Given a start vertex, what is the cheapest way to reach every other vertex, and how do we know when we have found it?
What a shortest path is once edges have weights
THE RUNNING EXAMPLE. ONE EDGE HAS A NEGATIVE WEIGHT.
δ(s, v) is the smallest weight of any path from s to v. If v cannot be reached from s, δ(s, v) = ∞.
TEAL: THE SHORTEST-PATH TREE FROM S, WITH δ(S, v) AT EACH VERTEX.
A CYCLE OF TOTAL WEIGHT −2.
One operation that every shortest-path algorithm repeats
def relax(u, v, w, d, parent): if d[u] + w < d[v]: d[v] = d[u] + w parent[v] = u
Every finite d[v] is the weight of a real path from s to v. So d[v] ≥ δ(s, v) at all times: estimates can be too high, never too low.
Bellman-Ford: every edge, over and over. Dijkstra (next): greedily, closest vertex first, using a priority queue.
RELAXING THE TEAL EDGES IN PATH ORDER IS ENOUGH.
Relax every edge, |V| − 1 times
If no negative cycle is reachable from s, then after |V| − 1 rounds d[v] = δ(s, v) for every vertex. It works with negative edge weights.
|V| − 1 rounds, each touching all |E| edges: O(V · E).
Edge order every round: C→D, A→C, B→C, B→D, B→A, S→A, S→B.
MAGENTA: ESTIMATES THAT CHANGED THIS ROUND.
| after round | S | A | B | C | D |
|---|---|---|---|---|---|
| start | 0 | ∞ | ∞ | ∞ | ∞ |
| 1 | 0 | 5 | 2 | ∞ | ∞ |
| 2 | 0 | 3 | 2 | 8 | 8 |
| 3 | 0 | 3 | 2 | 6 | 4 |
THE GRAPH AFTER ROUND 3.
| after round | S | A | B | C | D |
|---|---|---|---|---|---|
| start | 0 | ∞ | ∞ | ∞ | ∞ |
| 1 | 0 | 5 | 2 | ∞ | ∞ |
| 2 | 0 | 3 | 2 | 8 | 8 |
| 3 | 0 | 3 | 2 | 6 | 4 |
| 4 | 0 | 3 | 2 | 6 | 2 |
If an edge can still be relaxed after |V| − 1 rounds, some estimate is not final, which is only possible when a negative cycle is reachable from s.
def bellman_ford(vertices, edges, s): # edges: (u, v, w) d = {v: float('inf') for v in vertices} parent = {v: None for v in vertices} d[s] = 0 for _ in range(len(vertices) - 1): for u, v, w in edges: if d[u] + w < d[v]: # relax d[v] = d[u] + w parent[v] = u for u, v, w in edges: # check pass if d[u] + w < d[v]: raise ValueError('negative cycle') return d, parent
What to keep, and what comes next