Ford-Fulkerson Algorithm for Maximum Flow Problem

Last Updated : 20 Sep, 2026

Given a flow network represented as a directed graph with V vertices and E edges, where edges[i] = [u, v, w] denotes a directed edge from vertex u to vertex v with capacity w, find the maximum flow that can be sent from the source vertex 1 to the sink vertex V.

The flow through an edge cannot exceed its capacity. The maximum flow is the largest amount of flow that can be transferred from the source to the sink while satisfying all capacity constraints.

Input: V = 4, edges[][] = [[1, 2, 8], [1, 3, 10], [2, 4, 2], [3, 4, 3]]
Output: 5
Explanation:

2

The flow can be sent through the following paths:
1 -> 2 -> 4 carrying min(8, 2) = 2 units of flow.
1 -> 3 -> 4 carrying min(10, 3) = 3 units of flow.
Therefore, the maximum flow from vertex 1 to vertex 4 is: 2 + 3 = 5.

Input: V = 5, edges[][] = [[1, 2, 5], [1, 3, 4], [2, 4, 3], [2, 5, 2], [3, 4, 2], [4, 5, 5]]
Output: 7
Explanation:

1

The flow can be sent through the following paths:
1 -> 2 -> 5 carrying 2 units of flow.
1 -> 2 -> 4 -> 5 carrying 3 units of flow.
1 -> 3 -> 4 -> 5 carrying 2 units of flow.
Therefore, the maximum flow from vertex 1 to vertex 5 is: 2 + 3 + 2 = 7.

Note: For an introduction to the Max Flow Problem, refer to the Max Flow Problem Introduction article.

Greedily Push Flow - May Not Produce Optimal Flow

The idea is to repeatedly find a path from the source S to the sink T and send the maximum possible flow through it.

The flow that can be sent along a path is limited by the edge with the minimum remaining capacity on that path, known as the bottleneck edge.

However, this approach has a major drawback. Once flow is assigned to a path, it cannot be modified later. As a result, an early path selection may block better flow distributions and prevent the algorithm from finding the true maximum flow.

Consider the following flow network:

2056958395

The actual maximum flow is 20, achieved by sending 10 units through S -> A -> T and 10 units through S -> B -> T.

2056958396

If the first path chosen is S -> A -> B -> T, the algorithm sends 10 units of flow and gets stuck with a total flow of 10, which is not optimal.

2056958399

To overcome this limitation, the Ford-Fulkerson Algorithm uses a residual graph and reverse edges, allowing previously assigned flow to be adjusted and rerouted when a better path is found.

Ford-Fulkerson Algorithm - O(V^3 x E) time and O(V^2) space

The Ford-Fulkerson Algorithm repeatedly finds a path from the source to the sink through which additional flow can be sent. Such a path is called an augmenting path.

In this implementation, BFS is used to find an augmenting path in the residual graph.

Step 1: Start with initial flow as 0.

Step 2: While there exists an augmenting path from the source to the sink:  

  • Find an augmenting path using any path-finding algorithm, such as BFS or DFS. We have used BFS here and this implementation is called Edmonds-Karp Algorithm.
  • Find the amount of flow that can be sent along the augmenting path, which is the minimum residual capacity along the edges of the path.
  • Increase the flow along the augmenting path by the determined amount.

Step 3: Return the maximum flow.

The minimum residual capacity on an augmenting path is called the bottleneck capacity.

What is a Residual Graph?

The residual graph represents the remaining capacities after some flow has already been sent through the network. Whenever f units of flow are sent through an edge (u, v):

  • residual[u][v] -= f;
  • residual[v][u] += f;

Here:

  • residual[u][v] represents the remaining capacity of the forward edge.
  • residual[v][u] represents the capacity of the reverse edge.

The reverse capacity allows previously sent flow to be cancelled and redirected through another path when required.

What is a Reverse Edge?

A reverse edge is not an actual edge in the original graph. It is created in the residual graph whenever flow is sent through an edge.

  • Suppose an edge has capacity: A ----10----> B
  • If 6 units of flow are sent through it: A ----6/10----> B
  • Then the residual graph becomes: A ----4----> B and A <----6---- B

Here: A -> B = 4 represents the remaining capacity and B -> A = 6 is a reverse edge.

The reverse edge means: Up to 6 units of the previously assigned flow on A -> B can be canceled and rerouted through another path if required. Without reverse edges, flow decisions would become permanent, and the algorithm might miss the optimal solution.

We use reverse edges to update the residual capacities. When we send flow along a path, we subtract that flow from the forward edges and add the same amount to their reverse edges. T

he reverse capacity represents how much of the previously sent flow can later be cancelled or rerouted if a better augmenting path is found.

Example:

Consider the following flow network:

2056958395

The maximum possible flow is: 20

Iteration 1:

  • Suppose BFS finds the path: S -> A -> B -> T
  • The bottleneck capacity is: min(10, 10, 10) = 10, Send 10 units of flow.
  • Current flow: maxFlow = 10
  • Residual Graph After Iteration 1
file

Notice the reverse edge: B -> A = 10, which means the 10 units of flow currently passing through A -> B can be canceled if a better route is found.

Iteration 2:

  • The algorithm searches the residual graph and finds: S -> B -> A -> T, Here: B -> A is a reverse edge.
  • The bottleneck capacity is: min(10, 10, 10) = 10, Send 10 units of flow.
  • Using the reverse edge does not create new flow. Instead, it: Cancels the previous flow on: A -> B. Redirects that flow through: A -> T
  • As a result: maxFlow = 20
C++
#include <bits/stdc++.h>
using namespace std;

// Finds an augmenting path from source to sink in the residual graph
bool bfs(vector<vector<int>> &rGraph, int src, int sink,
         vector<int> &parent) {

    int V = rGraph.size();

    vector<bool> vis(V, false);
    queue<int> q;

    q.push(src);
    vis[src] = true;
    parent[src] = -1;

    while (!q.empty()) {

        int u = q.front();
        q.pop();

        // Explore all adjacent vertices
        for (int v = 1; v < V; v++) {

            // Visit only unvisited vertices having positive residual capacity
            if (!vis[v] && rGraph[u][v] > 0) {

                vis[v] = true;
                parent[v] = u;

                // Sink reached, augmenting path found
                if (v == sink)
                    return true;

                q.push(v);
            }
        }
    }

    return false;
}

// Returns the maximum flow from source (1) to sink (V)
int findMaxFlow(int V, vector<vector<int>> &edges) {

    // Capacity graph
    vector<vector<int>> graph(V + 1, vector<int>(V + 1, 0));

    for (auto &edge : edges) {

        int u = edge[0];
        int v = edge[1];
        int cap = edge[2];

        graph[u][v] = cap;
    }

    // Residual graph initially equals the capacity graph
    vector<vector<int>> rGraph = graph;

    int src = 1;
    int sink = V;

    vector<int> parent(V + 1);
    int maxFlow = 0;

    // Keep finding augmenting paths
    while (bfs(rGraph, src, sink, parent)) {

        int pathFlow = INT_MAX;

        // Find bottleneck capacity of the current path
        for (int v = sink; v != src; v = parent[v]) {

            int u = parent[v];
            pathFlow = min(pathFlow, rGraph[u][v]);
        }

        // Update residual capacities of forward and reverse edges
        for (int v = sink; v != src; v = parent[v]) {

            int u = parent[v];

            rGraph[u][v] -= pathFlow;
            rGraph[v][u] += pathFlow;
        }

        // Add path flow to the total maximum flow
        maxFlow += pathFlow;
    }

    return maxFlow;
}

int main() {

    int V = 4;

    vector<vector<int>> edges = {
        {1, 2, 10},
        {1, 3, 10},
        {2, 3, 10},
        {2, 4, 10},
        {3, 4, 10}
    };

    cout << findMaxFlow(V, edges) << endl;

    return 0;
}
Java
import java.util.*;

public class GFG {

    // Finds an augmenting path from source to sink in the residual graph
    static boolean bfs(int[][] rGraph, int src, int sink,
                       int[] parent) {

        int V = rGraph.length;

        boolean[] vis = new boolean[V];
        Queue<Integer> q = new LinkedList<>();

        q.offer(src);
        vis[src] = true;
        parent[src] = -1;

        while (!q.isEmpty()) {

            int u = q.poll();

            // Explore all adjacent vertices
            for (int v = 1; v < V; v++) {

                // Visit only unvisited vertices having positive residual capacity
                if (!vis[v] && rGraph[u][v] > 0) {

                    vis[v] = true;
                    parent[v] = u;

                    // Sink reached, augmenting path found
                    if (v == sink)
                        return true;

                    q.offer(v);
                }
            }
        }

        return false;
    }

    // Returns the maximum flow from source (1) to sink (V)
    static int findMaxFlow(int V, int[][] edges) {

        // Capacity graph
        int[][] graph = new int[V + 1][V + 1];

        for (int[] edge : edges) {

            int u = edge[0];
            int v = edge[1];
            int cap = edge[2];

            graph[u][v] = cap;
        }

        // Residual graph initially equals the capacity graph
        int[][] rGraph = new int[V + 1][V + 1];

        for (int i = 0; i <= V; i++)
            rGraph[i] = graph[i].clone();

        int src = 1;
        int sink = V;

        int[] parent = new int[V + 1];
        int maxFlow = 0;

        // Keep finding augmenting paths
        while (bfs(rGraph, src, sink, parent)) {

            int pathFlow = Integer.MAX_VALUE;

            // Find bottleneck capacity of the current path
            for (int v = sink; v != src; v = parent[v]) {

                int u = parent[v];
                pathFlow = Math.min(pathFlow, rGraph[u][v]);
            }

            // Update residual capacities of forward and reverse edges
            for (int v = sink; v != src; v = parent[v]) {

                int u = parent[v];

                rGraph[u][v] -= pathFlow;
                rGraph[v][u] += pathFlow;
            }

            // Add path flow to the total maximum flow
            maxFlow += pathFlow;
        }

        return maxFlow;
    }

    public static void main(String[] args) {

        int V = 4;

        int[][] edges = {
            {1, 2, 10},
            {1, 3, 10},
            {2, 3, 10},
            {2, 4, 10},
            {3, 4, 10}
        };

        System.out.println(findMaxFlow(V, edges));
    }
}
Python
from collections import deque
import sys

# Finds an augmenting path from source to sink in the residual graph
def bfs(rGraph, src, sink, parent):

    V = len(rGraph)

    vis = [False] * V
    q = deque()

    q.append(src)
    vis[src] = True
    parent[src] = -1

    while q:

        u = q.popleft()

        # Explore all adjacent vertices
        for v in range(1, V):

            # Visit only unvisited vertices having positive residual capacity
            if not vis[v] and rGraph[u][v] > 0:

                vis[v] = True
                parent[v] = u

                # Sink reached, augmenting path found
                if v == sink:
                    return True

                q.append(v)

    return False

# Returns the maximum flow from source (1) to sink (V)
def findMaxFlow(V, edges):

    # Capacity graph
    graph = [[0] * (V + 1) for _ in range(V + 1)]

    for edge in edges:

        u = edge[0]
        v = edge[1]
        cap = edge[2]

        graph[u][v] = cap

    # Residual graph initially equals the capacity graph
    rGraph = [row[:] for row in graph]

    src = 1
    sink = V

    parent = [0] * (V + 1)
    maxFlow = 0

    # Keep finding augmenting paths
    while bfs(rGraph, src, sink, parent):

        pathFlow = sys.maxsize

        # Find bottleneck capacity of the current path
        v = sink
        while v != src:

            u = parent[v]
            pathFlow = min(pathFlow, rGraph[u][v])
            v = parent[v]

        # Update residual capacities of forward and reverse edges
        v = sink
        while v != src:

            u = parent[v]

            rGraph[u][v] -= pathFlow
            rGraph[v][u] += pathFlow

            v = parent[v]

        # Add path flow to the total maximum flow
        maxFlow += pathFlow

    return maxFlow

V = 4

edges = [
    [1, 2, 10],
    [1, 3, 10],
    [2, 3, 10],
    [2, 4, 10],
    [3, 4, 10]
]

print(findMaxFlow(V, edges))
C#
using System;
using System.Collections.Generic;

class GFG
{
    // Finds an augmenting path from source to sink in the residual graph
    static bool bfs(int[,] rGraph, int src, int sink,
                    int[] parent)
    {
        int V = rGraph.GetLength(0);

        bool[] vis = new bool[V];
        Queue<int> q = new Queue<int>();

        q.Enqueue(src);
        vis[src] = true;
        parent[src] = -1;

        while (q.Count > 0)
        {
            int u = q.Dequeue();

            // Explore all adjacent vertices
            for (int v = 1; v < V; v++)
            {
                // Visit only unvisited vertices having positive residual capacity
                if (!vis[v] && rGraph[u, v] > 0)
                {
                    vis[v] = true;
                    parent[v] = u;

                    // Sink reached, augmenting path found
                    if (v == sink)
                        return true;

                    q.Enqueue(v);
                }
            }
        }

        return false;
    }

    // Returns the maximum flow from source (1) to sink (V)
    static int findMaxFlow(int V, int[,] edges)
    {
        // Capacity graph
        int[,] graph = new int[V + 1, V + 1];

        for (int i = 0; i < edges.GetLength(0); i++)
        {
            int u = edges[i, 0];
            int v = edges[i, 1];
            int cap = edges[i, 2];

            graph[u, v] = cap;
        }

        // Residual graph initially equals the capacity graph
        int[,] rGraph = (int[,])graph.Clone();

        int src = 1;
        int sink = V;

        int[] parent = new int[V + 1];
        int maxFlow = 0;

        // Keep finding augmenting paths
        while (bfs(rGraph, src, sink, parent))
        {
            int pathFlow = int.MaxValue;

            // Find bottleneck capacity of the current path
            for (int v = sink; v != src; v = parent[v])
            {
                int u = parent[v];
                pathFlow = Math.Min(pathFlow, rGraph[u, v]);
            }

            // Update residual capacities of forward and reverse edges
            for (int v = sink; v != src; v = parent[v])
            {
                int u = parent[v];

                rGraph[u, v] -= pathFlow;
                rGraph[v, u] += pathFlow;
            }

            // Add path flow to the total maximum flow
            maxFlow += pathFlow;
        }

        return maxFlow;
    }

    static void Main()
    {
        int V = 4;

        int[,] edges =
        {
            {1, 2, 10},
            {1, 3, 10},
            {2, 3, 10},
            {2, 4, 10},
            {3, 4, 10}
        };

        Console.WriteLine(findMaxFlow(V, edges));
    }
}
JavaScript
// Finds an augmenting path from source to sink in the residual graph
function bfs(rGraph, src, sink, parent) {

    const V = rGraph.length;

    const vis = new Array(V).fill(false);
    const q = [];

    q.push(src);
    vis[src] = true;
    parent[src] = -1;

    while (q.length > 0) {

        const u = q.shift();

        // Explore all adjacent vertices
        for (let v = 1; v < V; v++) {

            // Visit only unvisited vertices having positive residual capacity
            if (!vis[v] && rGraph[u][v] > 0) {

                vis[v] = true;
                parent[v] = u;

                // Sink reached, augmenting path found
                if (v === sink)
                    return true;

                q.push(v);
            }
        }
    }

    return false;
}

// Returns the maximum flow from source (1) to sink (V)
function findMaxFlow(V, edges) {

    // Capacity graph
    const graph = Array.from(
        { length: V + 1 },
        () => Array(V + 1).fill(0)
    );

    for (const edge of edges) {

        const u = edge[0];
        const v = edge[1];
        const cap = edge[2];

        graph[u][v] = cap;
    }

    // Residual graph initially equals the capacity graph
    const rGraph = graph.map(row => [...row]);

    const src = 1;
    const sink = V;

    const parent = new Array(V + 1);
    let maxFlow = 0;

    // Keep finding augmenting paths
    while (bfs(rGraph, src, sink, parent)) {

        let pathFlow = Number.MAX_SAFE_INTEGER;

        // Find bottleneck capacity of the current path
        for (let v = sink; v !== src; v = parent[v]) {

            const u = parent[v];
            pathFlow = Math.min(pathFlow, rGraph[u][v]);
        }

        // Update residual capacities of forward and reverse edges
        for (let v = sink; v !== src; v = parent[v]) {

            const u = parent[v];

            rGraph[u][v] -= pathFlow;
            rGraph[v][u] += pathFlow;
        }

        // Add path flow to the total maximum flow
        maxFlow += pathFlow;
    }

    return maxFlow;
}

const V = 4;

const edges = [
    [1, 2, 10],
    [1, 3, 10],
    [2, 3, 10],
    [2, 4, 10],
    [3, 4, 10]
];

console.log(findMaxFlow(V, edges));

Output
20

Note: Note: Using BFS to select augmenting paths makes this implementation the Edmonds-Karp variant of the Ford-Fulkerson method.

Other Algorithms to Solve Maximum Flow

Comment