Quick Info

Category: Graph
Time Complexity: O(VE)
Space Complexity: O(V)
Input Type: Weighted Graph

Bellman-Ford Algorithm

Description

The Bellman-Ford algorithm finds the shortest paths from a source vertex to all other vertices in a weighted graph. Unlike Dijkstra's algorithm, it can handle graphs with negative weight edges and detect negative weight cycles. The algorithm works by repeatedly relaxing all edges V-1 times, where V is the number of vertices.

  • Can detect negative cycles in the graph
  • Works with both positive and negative edge weights
  • Guarantees shortest paths in absence of negative cycles
  • Simpler but slower than Dijkstra's algorithm

History

Richard Bellman

The Bellman-Ford algorithm was independently discovered by Alfonso Shimbel (1955), Edward F. Moore (1957), and Richard Bellman (1958). However, it is most commonly named after Richard Bellman and Lester Ford Jr., who published it in 1958 and 1956 respectively. Richard Bellman made significant contributions to the field of dynamic programming, which is a key concept used in this algorithm. Lester Ford Jr. developed the algorithm while working on network flow problems at the RAND Corporation. His work was focused on solving transportation and logistics challenges, which naturally led to the development of efficient path-finding algorithms. The algorithm was later refined and popularized by Richard Bellman, who provided a more rigorous mathematical analysis and showed its connection to dynamic programming principles. The algorithm's ability to handle negative edge weights made it particularly valuable for applications beyond simple path finding, such as in financial arbitrage detection and network routing protocols. Today, it remains a fundamental algorithm in computer science, particularly in networking where it formed the basis for the RIP (Routing Information Protocol) used in early internet routing. The GOAT of dynamic programming.

Visualization

Select a source vertex to start Bellman-Ford algorithm

How It Works

  1. Initialize distances to all vertices as infinite except the source (distance = 0)
  2. Repeat V-1 times:
    • For each edge (u, v) with weight w:
    • If distance[u] + w < distance[v]:
    • Update distance[v] = distance[u] + w
  3. Check for negative weight cycles:
    • If any distance can be reduced further
    • Graph contains a negative weight cycle

Implementation

def bellman_ford(graph, source):
    # Initialize distances and predecessors
    distances = {vertex: float('infinity') for vertex in graph}
    predecessors = {vertex: None for vertex in graph}
    distances[source] = 0
    
    # Relax edges |V| - 1 times
    for _ in range(len(graph) - 1):
        for u in graph:
            for v, weight in graph[u].items():
                if distances[u] + weight < distances[v]:
                    distances[v] = distances[u] + weight
                    predecessors[v] = u
    
    # Check for negative weight cycles
    for u in graph:
        for v, weight in graph[u].items():
            if distances[u] + weight < distances[v]:
                raise ValueError("Graph contains a negative weight cycle")
    
    return distances, predecessors
#include <vector>
#include <limits>
#include <stdexcept>

struct Edge {
    int src, dest;
    int weight;
};

class BellmanFord {
public:
    static vector<int> FindShortestPaths(const vector<Edge>& edges, 
                                          int V, int source) {
        vector<int> distances(V, numeric_limits<int>::max());
        distances[source] = 0;
        
        // Relax edges |V| - 1 times
        for (int i = 0; i < V - 1; i++) {
            for (const Edge& edge : edges) {
                if (distances[edge.src] != numeric_limits<int>::max() &&
                    distances[edge.src] + edge.weight < distances[edge.dest]) {
                    distances[edge.dest] = distances[edge.src] + edge.weight;
                }
            }
        }
        
        // Check for negative weight cycles
        for (const Edge& edge : edges) {
            if (distances[edge.src] != numeric_limits<int>::max() &&
                distances[edge.src] + edge.weight < distances[edge.dest]) {
                throw runtime_error("Graph contains negative weight cycle");
            }
        }
        
        return distances;
    }
};
public class BellmanFord
{
    public class Edge
    {
        public int Source { get; set; }
        public int Destination { get; set; }
        public int Weight { get; set; }
    }

    public static Dictionary<int, int> FindShortestPaths(List<Edge> edges, 
                                                         int vertexCount, 
                                                         int source)
    {
        var distances = new Dictionary<int, int>();
        
        // Initialize distances
        for (int i = 0; i < vertexCount; i++)
        {
            distances[i] = int.MaxValue;
        }
        distances[source] = 0;
        
        // Relax edges |V| - 1 times
        for (int i = 0; i < vertexCount - 1; i++)
        {
            foreach (var edge in edges)
            {
                if (distances[edge.Source] != int.MaxValue &&
                    distances[edge.Source] + edge.Weight < distances[edge.Destination])
                {
                    distances[edge.Destination] = distances[edge.Source] + edge.Weight;
                }
            }
        }
        
        // Check for negative weight cycles
        foreach (var edge in edges)
        {
            if (distances[edge.Source] != int.MaxValue &&
                distances[edge.Source] + edge.Weight < distances[edge.Destination])
            {
                throw new InvalidOperationException(
                    "Graph contains a negative weight cycle");
            }
        }
        
        return distances;
    }
}

Complexity Analysis

Operation Time Complexity Space Complexity
Worst Case O(VE) O(V)
Average Case O(VE) O(V)
Best Case O(E) O(V)

Where V is the number of vertices and E is the number of edges in the graph.

Advantages and Disadvantages

Advantages

  • Handles negative edge weights
  • Detects negative cycles
  • Simple to implement
  • Works with any graph structure
  • Guarantees shortest paths if no negative cycles

Disadvantages

  • Slower than Dijkstra's algorithm
  • O(VE) complexity can be high for dense graphs
  • Not suitable for graphs with negative cycles
  • Requires all edges to be processed V-1 times
  • Higher memory usage than some alternatives

Applications

  • Network routing protocols (like RIP)
  • Currency exchange and arbitrage detection
  • Traffic routing with potential negative costs
  • Network delay analysis
  • Constraint satisfaction problems