GeeksforGeeks Medium

Bellman–Ford Algorithm

Shortest paths from a source that tolerates negative edges, and detects negative cycles.

graphshortest-pathdpnegative-weights
Open on GeeksforGeeks ↗
02

Intuition

Forget clever ordering — just relax every edge, over and over. After one full pass, all shortest paths using at most one edge are correct; after two passes, at most two edges; and since no shortest path can use more than V−1 edges, V−1 passes settle everything. If a V-th pass still improves something, that path is being fed by a negative cycle.

How to spot this pattern

Relax every edge V-1 times. A shortest path has at most V-1 edges, and each pass guarantees one more edge's worth of correctness. Unlike Dijkstra it tolerates negative weights, and a V-th pass that still improves something proves a negative cycle exists.

03

Approach

1

Relax everything, V−1 times

For each pass, loop over every edge (u, v, w) and apply dist[v] = min(dist[v], dist[u] + w). No priority queue and no ordering assumptions.

2

Why V−1 passes suffice

A shortest path is simple, so it has at most V−1 edges. Pass k fixes all paths of k edges, so after V−1 passes every shortest path is final.

3

Negative cycle detection

Run one extra pass. If any edge still relaxes, some path can be made arbitrarily cheap by looping — report a negative cycle rather than a distance.

04

Solution & live demo

1def bellman_ford(n, edges, src):
2 dist = [float("inf")] * n
3 dist[src] = 0
4 for _ in range(n - 1): # V-1 passes
5 changed = False
6 for u, v, w in edges: # relax every edge
7 if dist[u] + w < dist[v]:
8 dist[v] = dist[u] + w
9 changed = True
10 if not changed: # settled early
11 break
12 for u, v, w in edges: # one more pass -> negative cycle?
13 if dist[u] + w < dist[v]:
14 return None
15 return dist
05

Common pitfalls

Skipping the extra detection pass

✗ Wrong
return dist
✓ Right
for u, v, w in edges:
    if dist[u] + w < dist[v]:
        return None
return dist

After V-1 passes all genuine shortest paths are final, so any further improvement can only come from a cycle of negative total weight. Without that check the returned distances are meaningless on such graphs.

Relaxing from an unreachable vertex

✗ Wrong
if dist[u] + w < dist[v]:
✓ Right
if dist[u] != INF and dist[u] + w < dist[v]:

In Python inf + w stays inf so it happens to be safe, but in C++/Java adding to a large sentinel overflows to a negative number and floods the array with bogus distances. Guard the source explicitly.

Running only V-1 passes without the early exit

✗ Wrong
for _ in range(n - 1):
    for u, v, w in edges: ...
✓ Right
changed = False
...
if not changed:
    break

Correct but wasteful — most graphs settle long before V-1 passes. The changed flag turns the worst-case bound into a typical-case fast exit at no cost to correctness.

06

Edge cases

Negative cycle present

The extra pass still improves — report it; distances are meaningless.

Early convergence

If a pass changes nothing, distances are final — break out early.

Unreachable vertex

Stays ∞; guard against ∞ + w overflow in languages without float infinity.

07

Complexity

Time
O(V·E)
Space
O(V)
Slower than Dijkstra, but handles negative weights.