G-41: Bellman Ford Algorithm
The Bellman-Ford algorithm is a single-source shortest path algorithm capable of finding the shortest path from a starting node to all other nodes in a graph, even when Negative Edge Weights are present. Unlike Dijkstra's algorithm—which fails or falls into Time Limit Exceeded (TLE) loops when encountering negative weights or negative cycles—Bellman-Ford correctly computes shortest paths and explicitly detects negative cycles.
1. Why Do We Need Bellman-Ford? (Limitations of Dijkstra)
While Dijkstra's algorithm is efficient (), it relies on a greedy assumption: once a node is popped from the priority queue, its shortest distance is finalized. This assumption breaks down under certain conditions:
2. Core Mental Model: The Chain Reaction Analogy
To understand how shortest paths propagate through a graph, imagine a row of dominoes or a relay race where information travels sequentially.
- The Source Node () starts with a distance of .
- In Iteration 1, node can update its direct neighbors (e.g., node ).
- In Iteration 2, node (having now been reached and updated) can pass its accumulated distance to its neighbors (e.g., node or node ).
- Each complete pass across all edges allows shortest-path information to propagate one hop further across the graph.
3. Handling Undirected vs. Directed Graphs
The Bellman-Ford algorithm is designed strictly for Directed Graphs (DG).
Converting Undirected Graphs to Directed Graphs
If you are given an undirected edge between node 1 and node 2 with weight 5:
- You cannot apply Bellman-Ford directly.
- Solution: Break the undirected edge into two directed edges: one going from
1 -> 2(weight5) and one going from2 -> 1(weight5).
4. The Core Operation: Edge Relaxation
The foundational rule of Bellman-Ford is Edge Relaxation.
Relaxation Definition
For any directed edge from node to node with edge weight :
If: distance[U] + wt < distance[V]
Then: distance[V] = distance[U] + wt
In plain English: If the distance to reach node plus the cost of traveling from to is strictly less than our current best known distance to reach , we have found a shorter path. Update 's distance.
5. Why Exactly Iterations?
In a graph with vertices, the longest possible shortest path without repeating any vertex (i.e., a simple path) can contain at most edges.
- In the worst-case linear chain (where node at the bottom depends on node above it, which depends on the one above it, down to the source), each iteration of relaxing all edges guarantees that the shortest path information flows down by at least one hop.
- Therefore, after complete iterations over all edges, all shortest paths across any valid graph structure are guaranteed to be fully computed.
6. Detecting Negative Cycles
A negative cycle is a closed loop in a graph where the sum of edge weights is less than .
The Detection Rule
- Run the standard Bellman-Ford relaxation process for iterations.
- Perform one extra () iteration over all edges.
- If any edge can still be relaxed (i.e.,
distance[U] + wt < distance[V]is still true), it proves that a negative cycle exists, because shortest paths should stop shrinking after iterations unless trapped in a negative weight loop.
7. Step-by-Step Algorithm Walkthrough
- Initialize Distance Array: Set
distance[source] = 0and all other nodes to1e8(Infinity). - Edge List Format: Store all directed edges as tuples:
(u, v, weight). Edges can be provided in any arbitrary, jumbled order. - Main Loop ( times):
- Loop through every edge
(u, v, wt)in the edge list. - Apply the relaxation check.
- Loop through every edge
- Negative Cycle Check ( iteration):
- Loop through all edges one more time.
- If any distance updates, return an error state (or array of
-1).
8. Complete Implementation (C++ & Java Equivalents)
Below is the complete production-ready implementation of the Bellman-Ford algorithm, handling source distances, edge relaxations, and negative cycle detection.
C++ Implementation
class Solution {
public:
vector<int> bellman_ford(int V, vector<vector<int>>& edges, int S) {
vector<int> dist(V, 1e8);
dist[S] = 0;
// Step 1: Relax all edges V - 1 times
for (int i = 0; i < V - 1; i++) {
for (auto it : edges) {
int u = it[0];
int v = it[1];
int wt = it[2];
if (dist[u] != 1e8 && dist[u] + wt < dist[v]) {
dist[v] = dist[u] + wt;
}
}
}
// Step 2: Nth relaxation to check for negative cycle
for (auto it : edges) {
int u = it[0];
int v = it[1];
int wt = it[2];
if (dist[u] != 1e8 && dist[u] + wt < dist[v]) {
// Negative cycle detected! Return {-1}
return {-1};
}
}
return dist;
}
};
9. Complexity Analysis
- Time Complexity:
- We iterate times, and in each iteration, we traverse all edges. In the worst-case dense graph where , the time complexity approaches .
- Space Complexity:
- Required for maintaining the
distancearray of size (plus storage for the edge list).
- Required for maintaining the
Summary and Key Takeaways
- Shortest Path with Negative Edges: Bellman-Ford successfully computes single-source shortest paths even when negative edge weights exist.
- Negative Cycle Detection: Unlike Dijkstra, Bellman-Ford identifies negative cycles via a edge relaxation check.
- Directed Graph Requirement: The algorithm operates strictly on directed graphs; undirected graphs must be converted by splitting every edge into two directed edges.
- Sequential Edge Relaxation: All edges must be relaxed sequentially across iterations to allow propagation across the longest possible simple path.
- Why Iterations?: In the worst-case chain graph, information takes hops to travel from the top node to the bottom node.
- Time Complexity: , making it slower than Dijkstra's algorithm (), but necessary when negative weights are present.
- Error Return: If a negative cycle is detected during the extra relaxation pass, the standard convention is to return an array containing
-1.