Bellman-Ford Algorithm
Overview
The Bellman-Ford algorithm is a graph search algorithm that computes shortest paths from a single source vertex to all other vertices in a weighted directed graph. Unlike Dijkstra's algorithm, Bellman-Ford can handle graphs with negative edge weights and can detect negative cycles.
The algorithm works by relaxing all edges repeatedly. It performs V-1 iterations (where V is the number of vertices), and in each iteration, it relaxes all edges. If after V-1 iterations, we can still relax an edge, it means there's a negative cycle in the graph.
Bellman-Ford is particularly useful in network routing protocols, currency arbitrage detection, and any scenario where negative weights are present or need to be detected.
How It Works
The algorithm follows these steps:
- Initialize: Set distance to source as 0, and all other distances as infinity
- Relax Edges: Repeat V-1 times:
- For each edge (u, v) with weight w
- If dist[u] + w < dist[v], update dist[v] = dist[u] + w
- Check for Negative Cycles: After V-1 iterations, check if any edge can still be relaxed. If yes, negative cycle exists.
- Result: Distances array contains shortest distances (or indicates negative cycle)
Bellman-Ford Algorithm Pseudocode
BellmanFord(graph, source):
dist[source] = 0
dist[v] = ∞ for all other vertices v
parent[v] = null for all vertices
// Relax edges V-1 times
for i = 1 to V-1:
for each edge (u, v) with weight w in graph:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
parent[v] = u
// Check for negative cycles
for each edge (u, v) with weight w in graph:
if dist[u] + w < dist[v]:
return "Negative cycle detected"
return dist, parent
Implementation
def bellman_ford(edges, n, start):
"""
edges: list of edges [(u, v, weight), ...]
n: number of vertices, passed in explicitly
start: source vertex
Returns: (distances, parent, has_negative_cycle)
n MUST be a parameter. Deriving it with len(set(...)) over the edge list
counts only vertices that appear in some edge, so any isolated vertex is
dropped - which makes the array too short and the V-1 iteration count wrong.
"""
dist = [float('inf')] * n
dist[start] = 0
parent = [-1] * n
# Relax all edges V-1 times
for _ in range(n - 1):
changed = False
for u, v, w in edges:
if dist[u] != float('inf') and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
parent[v] = u
changed = True
if not changed:
break # early exit: a pass that relaxes nothing means we are done
# An edge still relaxable after V-1 passes implies a negative cycle
has_negative_cycle = any(
dist[u] != float('inf') and dist[u] + w < dist[v]
for u, v, w in edges
)
return dist, parent, has_negative_cycle
def reconstruct_path(parent, target):
"""Reconstruct shortest path from parent array"""
path = []
current = target
while current != -1:
path.append(current)
current = parent[current]
return path[::-1] if path[0] == target else []
Complexity Analysis
- Time Complexity: O(V × E) where V is vertices and E is edges
- Space Complexity: O(V) - for distance and parent arrays
The algorithm is slower than Dijkstra's (O((V + E) log V)) but can handle negative weights and detect negative cycles, which Dijkstra cannot.
Negative Cycles
A negative cycle is a cycle in the graph where the sum of edge weights is negative. If such a cycle is reachable from the source, the shortest path is undefined (can be made arbitrarily short by going around the cycle).
Bellman-Ford detects negative cycles by checking if any edge can still be relaxed after V-1 iterations. Any shortest path visits at most V-1 edges, so after V-1 passes everything should have settled; if an edge still improves, no shortest path exists.
Two cautions when a negative cycle is reported. First, the returned dist array is
meaningless in that case — do not use it. Second, only cycles
reachable from the source are detected. To identify which vertices are affected,
run V-1 further passes and mark every vertex still being relaxed, then propagate that mark to
everything reachable from them — those are the vertices whose true distance is −∞.
Note also that the algorithm assumes a directed graph. A negative-weight edge in an undirected graph is a negative cycle, since you can traverse it back and forth indefinitely.
Example
Finding shortest paths from vertex 0:
Graph (5 vertices, one negative edge):
0 --(4)--> 1 --(3)--> 3
| ^
(1) |
| (-2)
v |
2 ---------------------+
|
(2)
v
4
Edges: 0->1 (4), 0->2 (1), 1->3 (3), 2->3 (-2), 2->4 (2)
V = 5, so we run V-1 = 4 relaxation passes, then a 5th checking pass.
Pass 1: relax every edge
dist[0] = 0 source
dist[1] = 0 + 4 = 4 via 0->1
dist[2] = 0 + 1 = 1 via 0->2
dist[3] = 1 + (-2) = -1 via 2->3 (beats 4 + 3 = 7 via 1->3)
dist[4] = 1 + 2 = 3 via 2->4
Pass 2: no edge improves -> early exit
Check pass: no edge can be relaxed -> no negative cycle
Result: dist = [0, 4, 1, -1, 3]
Note dist[4] = 3, reached as 0→2→4 at cost 1 + 2 — vertex 4 hangs off
vertex 2, not vertex 3.
When to Use Bellman-Ford
Bellman-Ford is ideal when:
- Graph has negative edge weights
- You need to detect negative cycles
- Graph is sparse (few edges)
- Currency arbitrage detection
- Network routing with negative costs
Consider alternatives when:
- All weights are non-negative → Use Dijkstra's (faster)
- Unweighted graph → Use BFS (simpler)
- All-pairs shortest paths → Use Floyd-Warshall
Bellman-Ford vs Other Algorithms
| Algorithm | Negative Weights | Time Complexity | Best For |
|---|---|---|---|
| Dijkstra's | No | O((V + E) log V) | Non-negative weights |
| Bellman-Ford | Yes | O(V × E) | Negative weights, cycle detection |
| Floyd-Warshall | Yes | O(V³) | All-pairs shortest paths |
Real-World Applications
- Currency Arbitrage: Detecting profitable currency exchange cycles
- Network Routing: Routing with negative costs (e.g., refunds)
- Game Theory: Finding optimal strategies with negative payoffs
- Resource Allocation: Optimizing with negative costs
Related Algorithms
Explore other searching algorithms:
- Dijkstra's Algorithm - For non-negative weights
- Floyd-Warshall Algorithm - All-pairs shortest paths
- A* Algorithm - Heuristic-based search
- Back to Graph Algorithms Overview