Quick Info

Category: Graph
Time Complexity: O(VE²)
Space Complexity: O(V²)
Input Type: Flow Network

Ford-Fulkerson Algorithm

Description

The Ford-Fulkerson algorithm is a method for computing the maximum flow in a flow network. It works by finding augmenting paths through the residual graph and adding flow along these paths. The algorithm repeatedly finds augmenting paths and adds flow until no more augmenting paths can be found.

History

Ford and Fulkerson

The algorithm was first published in 1956 by L. R. Ford, Jr. and D. R. Fulkerson. It was developed during their time at RAND Corporation, where they were working on Soviet railway traffic flow and trying to identify which routes would be most disruptive if blocked. The algorithm has since become a fundamental tool in network flow theory and has applications in transportation, telecommunications, and many other fields.

How It Works

The Ford-Fulkerson algorithm uses an iterative approach to find the maximum flow:

  1. Initialization:
    • Set initial flow to 0 for all edges
    • Create initial residual graph equal to the original graph
  2. Finding Augmenting Path:
    • Use BFS or DFS to find a path from source to sink
    • Path must have positive residual capacity in all edges
  3. Flow Update:
    • Find the minimum possible flow along the augmenting path
    • Update flow in both original and residual graphs
  4. Iteration:
    • Repeat steps 2 and 3 until no more augmenting paths are found
    • The sum of all augmenting flows is the maximum flow

Visualization

Current Step

Click Start to begin

Current Flow

0

Path Found

None

Implementation


from collections import defaultdict

class Graph:
    def __init__(self, vertices):
        self.V = vertices
        self.graph = [[0] * vertices for _ in range(vertices)]
        
    def bfs(self, s, t, parent):
        visited = [False] * self.V
        queue = []
        
        queue.append(s)
        visited[s] = True
        
        while queue:
            u = queue.pop(0)
            for v in range(self.V):
                if not visited[v] and self.graph[u][v] > 0:
                    queue.append(v)
                    visited[v] = True
                    parent[v] = u
        
        return visited[t]
    
    def ford_fulkerson(self, source, sink):
        parent = [-1] * self.V
        max_flow = 0
        
        while self.bfs(source, sink, parent):
            path_flow = float("Inf")
            s = sink
            
            while s != source:
                path_flow = min(path_flow, self.graph[parent[s]][s])
                s = parent[s]
            
            max_flow += path_flow
            
            v = sink
            while v != source:
                u = parent[v]
                self.graph[u][v] -= path_flow
                self.graph[v][u] += path_flow
                v = parent[v]
        
        return max_flow

# Example usage
if __name__ == "__main__":
    g = Graph(6)
    g.graph = [[0, 16, 13, 0, 0, 0],
               [0, 0, 10, 12, 0, 0],
               [0, 4, 0, 0, 14, 0],
               [0, 0, 9, 0, 0, 20],
               [0, 0, 0, 7, 0, 4],
               [0, 0, 0, 0, 0, 0]]
    
    source, sink = 0, 5
    print(f"Maximum flow from source {source} to sink {sink}: "
          f"{g.ford_fulkerson(source, sink)}")
                                        

#include <iostream>
#include <limits.h>
#include <queue>
#include <string.h>
using namespace std;

class Graph {
    int V;
    int **graph;
    
    bool bfs(int s, int t, int parent[]) {
        bool *visited = new bool[V];
        memset(visited, 0, sizeof(bool) * V);
        
        queue<int> q;
        q.push(s);
        visited[s] = true;
        parent[s] = -1;
        
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            
            for (int v = 0; v < V; v++) {
                if (!visited[v] && graph[u][v] > 0) {
                    if (v == t) {
                        parent[v] = u;
                        delete[] visited;
                        return true;
                    }
                    q.push(v);
                    parent[v] = u;
                    visited[v] = true;
                }
            }
        }
        
        delete[] visited;
        return false;
    }
    
public:
    Graph(int V) {
        this->V = V;
        graph = new int*[V];
        for (int i = 0; i < V; i++) {
            graph[i] = new int[V];
            memset(graph[i], 0, sizeof(int) * V);
        }
    }
    
    void addEdge(int u, int v, int w) {
        graph[u][v] = w;
    }
    
    int fordFulkerson(int s, int t) {
        int *parent = new int[V];
        int max_flow = 0;
        
        while (bfs(s, t, parent)) {
            int path_flow = INT_MAX;
            for (int v = t; v != s; v = parent[v]) {
                int u = parent[v];
                path_flow = min(path_flow, graph[u][v]);
            }
            
            for (int v = t; v != s; v = parent[v]) {
                int u = parent[v];
                graph[u][v] -= path_flow;
                graph[v][u] += path_flow;
            }
            
            max_flow += path_flow;
        }
        
        delete[] parent;
        return max_flow;
    }
    
    ~Graph() {
        for (int i = 0; i < V; i++)
            delete[] graph[i];
        delete[] graph;
    }
};

int main() {
    Graph g(6);
    g.addEdge(0, 1, 16);
    g.addEdge(0, 2, 13);
    g.addEdge(1, 2, 10);
    g.addEdge(1, 3, 12);
    g.addEdge(2, 1, 4);
    g.addEdge(2, 4, 14);
    g.addEdge(3, 2, 9);
    g.addEdge(3, 5, 20);
    g.addEdge(4, 3, 7);
    g.addEdge(4, 5, 4);
    
    cout << "Maximum flow: " << g.fordFulkerson(0, 5) << endl;
    return 0;
}
                                        

public class Graph
{
    private int V;
    private int[,] graph;
    
    public Graph(int vertices)
    {
        V = vertices;
        graph = new int[V, V];
    }
    
    private bool BFS(int s, int t, int[] parent)
    {
        bool[] visited = new bool[V];
        Queue<int> queue = new Queue<int>();
        
        queue.Enqueue(s);
        visited[s] = true;
        parent[s] = -1;
        
        while (queue.Count > 0)
        {
            int u = queue.Dequeue();
            
            for (int v = 0; v < V; v++)
            {
                if (!visited[v] && graph[u, v] > 0)
                {
                    queue.Enqueue(v);
                    parent[v] = u;
                    visited[v] = true;
                }
            }
        }
        
        return visited[t];
    }
    
    public int FordFulkerson(int source, int sink)
    {
        int[] parent = new int[V];
        int maxFlow = 0;
        
        while (BFS(source, sink, parent))
        {
            int pathFlow = int.MaxValue;
            
            for (int v = sink; v != source; v = parent[v])
            {
                int u = parent[v];
                pathFlow = Math.Min(pathFlow, graph[u, v]);
            }
            
            for (int v = sink; v != source; v = parent[v])
            {
                int u = parent[v];
                graph[u, v] -= pathFlow;
                graph[v, u] += pathFlow;
            }
            
            maxFlow += pathFlow;
        }
        
        return maxFlow;
    }
    
    public void AddEdge(int u, int v, int w)
    {
        graph[u, v] = w;
    }
}

public class Program
{
    public static void Main()
    {
        Graph g = new Graph(6);
        g.AddEdge(0, 1, 16);
        g.AddEdge(0, 2, 13);
        g.AddEdge(1, 2, 10);
        g.AddEdge(1, 3, 12);
        g.AddEdge(2, 1, 4);
        g.AddEdge(2, 4, 14);
        g.AddEdge(3, 2, 9);
        g.AddEdge(3, 5, 20);
        g.AddEdge(4, 3, 7);
        g.AddEdge(4, 5, 4);
        
        Console.WriteLine($"Maximum flow: {g.FordFulkerson(0, 5)}");
    }
}
                                        

Complexity Analysis

Time Complexity

The time complexity of Ford-Fulkerson algorithm is O(VE²), where V is the number of vertices and E is the number of edges. This can be broken down into:

  • BFS/DFS Search: O(V + E)
    • Each search visits all vertices and edges once
    • Used to find augmenting paths
  • Maximum Flow: O(E)
    • Each augmenting path can increase flow by at least 1
    • Maximum flow is bounded by O(E × maxCapacity)
  • Number of Iterations: O(VE)
    • In worst case, each path augments flow by 1
    • Total number of augmenting paths can be O(VE)

Space Complexity

The space complexity is O(V²), which includes:

  • Residual Graph: O(V²)
    • Adjacency matrix representation
    • Stores flow capacities between all pairs of vertices
  • Parent Array: O(V)
    • Stores path information during BFS/DFS
  • Visited Array: O(V)
    • Used in BFS/DFS to track visited vertices

Advantages and Disadvantages

Advantages

  • Simple and intuitive implementation
  • Works well with integer capacities
  • Can be modified for different flow problems
  • Provides both maximum flow and minimum cut
  • Easy to understand and implement with BFS

Disadvantages

  • May be slow for large networks
  • Not optimal for real-valued capacities
  • Time complexity depends on flow values
  • Can be inefficient compared to push-relabel
  • Memory intensive for dense graphs