Maximum flow (Edmonds-Karp)
Find the most that can flow from a source to a sink through edges with capacity limits, by repeatedly pushing flow along the shortest path that still has room.
O(n * e) rounds of BFSSpace O(n²)2 practice problemsStep-through pictureThe idea
Picture a network of pipes, each with a limit on how much water it can carry per second, called its capacity. Water enters at a node called the source and leaves at a node called the sink. The maximum flow is the most water per second that can get from source to sink without any pipe going over its limit.
The Ford-Fulkerson idea: find any path from source to sink where every pipe still has spare room (an augmenting path), push as much as the tightest pipe on it allows (the bottleneck), and repeat until no such path exists. Edmonds-Karp is the version that finds each path with BFS, which keeps the number of rounds small.
The clever part is undo edges. When you push flow along a → b, you also add that much spare room on b → a. A later path may use b → a to cancel some earlier flow and send it a better way. Without undo edges, an early bad choice could block the true maximum.
An everyday example
Moving people through a building with narrow doors
A crowd must move from the lobby to the exit through rooms joined by doors, and each door only lets a certain number of people through per minute. You keep finding a route with spare room at every door and send as many people as the narrowest door allows. Sometimes you realize a group should have gone another way, so you redirect them, freeing up a door for a new route. When no route has spare room left, you've found the most people per minute the building can move.
| In the example | In the code |
|---|---|
| A door's limit | cap[from][to] |
| A route with spare room at every door | A BFS path where every cap is above 0 |
| The narrowest door on the route | bottleneck |
| Redirecting people who took a door | Adding to the undo edge cap[v][parent[v]] |
| People moved per minute | flow |
From the slow way to maximum flow (edmonds-karp)
- Start with the obvious greedy way: find any path from source to sink where every edge has room, push as much as the tightest edge allows, and repeat until no such path exists. It sounds right, but an early path can take room that a better plan needed. In the trace's graph, this greedy method stops at 4 when the true maximum is 5.
- The fix is to let later paths undo earlier choices. When
bunits are pushed alonga → b, addbunits of room to the opposite direction,b → a. A later path that usesb → ais really saying "send less alonga → band reroute it". With these undo edges, getting stuck finally means the flow can't grow at all. - Which path should each round use? Any path works when capacities are whole numbers, because each round adds at least 1. But with a bad choice, the number of rounds can be as large as the answer itself, which might be millions.
- Choosing the path with the fewest edges, found by BFS, fixes that: the distance from the source to each node never goes down, and the number of rounds is at most
O(n · e)whatever the capacities are. BFS for a path, find its bottleneck, push it and add undo room, repeat: that loop is Edmonds-Karp.
How it works
- Make a capacity table
cap[from][to]and add each edge's capacity to it. - Run BFS from
sourceover edges withcap > 0, recording each node'sparent. - If the BFS never reaches
sink, no more flow can get through: returnflow. - Walk back from
sinkalongparentto find thebottleneck, the smallest spare capacity on the path. - Walk back again: lower each forward
capbybottleneckand raise each undocapby the same amount. - Add
bottlenecktoflowand search for another path.
Watch it run
This picture runs the code below on a real input. Press Step to move one step at a time, or Play to watch. The highlighted line is the one that just ran. Change the input to see other cases.
See every step at onceThe same run, laid out side by side
The code
Point at a note to light up the lines it explains.
function maxFlow(n, edges, source, sink) {const cap = Array.from({ length: n }, () => new Array(n).fill(0));for (const [from, to, capacity] of edges) cap[from][to] += capacity;let flow = 0;while (true) {const parent = new Array(n).fill(-1);parent[source] = source;const queue = [source];for (let head = 0; head < queue.length && parent[sink] === -1; head++) {const node = queue[head];for (let next = 0; next < n; next++) {if (parent[next] === -1 && cap[node][next] > 0) {parent[next] = node;queue.push(next);}}}if (parent[sink] === -1) return flow;let bottleneck = Infinity;for (let v = sink; v !== source; v = parent[v]) bottleneck = Math.min(bottleneck, cap[parent[v]][v]);for (let v = sink; v !== source; v = parent[v]) {cap[parent[v]][v] -= bottleneck;cap[v][parent[v]] += bottleneck;}flow += bottleneck;}}
How fast it is
| Time | O(n * e) rounds of BFS |
|---|---|
| Extra memory | O(n²) |
Edmonds-Karp needs at most O(n * e) augmenting paths, each found by one BFS. The capacity table has one cell for every pair of nodes.
- Best case
O(n²)The sink can't be reached at all, so one BFS over the
nbyntable ends it. Building the table already costsn². - Average case
O(k · n²)kis the number of rounds. Each round is one BFS scanning matrix rows,n²steps, and typical inputs need only a few rounds, far below the bound. - Worst case
O(n³ · e)Inputs built to force about
n · erounds, each with a fulln²BFS. It's a bound from theory; real inputs rarely come close.
Common mistakes
- Leaving out the undo edges. The greedy paths can then get stuck below the true maximum.
- Overwriting
cap[from][to]instead of adding to it when the same edge appears twice. - Using DFS with no limits on graphs with large capacities, which can need a huge number of rounds. BFS (Edmonds-Karp) keeps it bounded.
- Forgetting that the matrix makes memory grow with
n². For many thousands of nodes, use adjacency lists with paired edge objects.
When to use it
Look for these clues in a problem:
- Edges have capacities, limits, or bandwidth, and the question asks for the most that can get through.
- You need to pair items from two groups as much as possible, with each item used at most once (bipartite matching).
- The question asks for the fewest edges or the cheapest set of edges to cut so the source can't reach the sink (min cut equals max flow).
- You need the most paths from one node to another that share no edge.
Check yourself
Answer each one in your head first, then open it.
Why does pushing flow add room to the opposite direction?
So a later path can cancel some of that flow and send it elsewhere. Without it, an early choice can block the true maximum, as the greedy method stuck at 4 in the trace shows.
Why use BFS rather than any path search?
Shortest paths guarantee at most O(n · e) rounds whatever the capacities are. Any path can need as many rounds as the answer's size.
When BFS fails, how do you find the minimum cut?
Take the nodes BFS reached. The original edges from them to unreached nodes are all full, and their capacities add up to the maximum flow.
In the trace, how much flow ends up on the edge 3 → 1?
2. Round 2 sent 3 units along it, and round 3 cancelled 1 by pushing along the undo edge 1 → 3.
Practice problems
Stuck on one? Tuto is in the playground too, and gives you hints, never the answer.
0 of 2 solved. Solve one to earn the bronze medal, half for silver, and all of them for gold.
Start practicingGo further
You don't need these to start practicing. Open one when you want more.
Why it works
The reasoning that makes it always right
The rule kept after every round: the flow on each original edge, which is its capacity minus its cap, stays between 0 and the capacity, and every node except the source and sink has as much flow in as out. A round adds bottleneck along a whole path, entering and leaving each middle node by the same amount, and bottleneck is no larger than any cap on the path, so no cap goes below 0.
When BFS can't reach the sink, call the set of reached nodes S. Every edge from S to the rest has cap 0. For an original edge, that means it's full. For an undo edge, it means the matching original edge from the rest into S carries no flow. So the flow from source to sink equals the total capacity of the edges leaving S.
No flow can ever be bigger than the capacity of a cut like S, because everything that reaches the sink has to cross it. The current flow equals one cut's capacity, so it's the maximum, and that cut is the smallest. This is the max-flow min-cut theorem. The loop ends because Edmonds-Karp needs at most O(n · e) rounds.
Going deeper
3 more ideas
Why it stops at the maximum
When BFS can't reach the sink, let S be the nodes it did reach. Every original edge from S to the rest is full, and every edge coming back into S carries no flow (or its undo edge would have room). So the flow equals the total capacity of the edges leaving S. No flow can ever be bigger than the capacity of any such cut, so this flow is the maximum. This is the max-flow min-cut theorem.
Cost
Using BFS means each path found has the fewest edges, and it can be shown that this limits the number of rounds to O(n * e). This template finds neighbors by scanning a matrix row, so each BFS costs O(n²), for a total of O(n³ * e) in theory. In practice it is fast for the few hundred nodes these problems usually have; adjacency lists make each BFS O(n + e).
Matching with flow
To match reviewers to pull requests, add a source with an edge of capacity 1 to every reviewer, an edge of capacity 1 from each reviewer to each pull request they can review, and an edge of capacity 1 from every pull request to a sink. The maximum flow is the largest number of pairs, because each unit of flow is one reviewer matched to one pull request.
Work it by hand
Every step in a table, with why
Six nodes, source 0, sink 5. The third round sends flow backwards along an undo edge to reroute one unit, which the plain greedy method can't do.
n = 6, edges = [[0,3,4], [0,4,4], [3,5,1], [3,1,4], [3,2,1], [4,1,1], [1,5,3], [2,5,2]], source = 0, sink = 5
| Step | Round | Path or update | bottleneck | flow | What happens | Why |
|---|---|---|---|---|---|---|
| 1 | 1 | 0→3→5 | 1 | 0 | BFS finds 2 edges | The shortest path with room uses 0 → 3 (room 4) and 3 → 5 (room 1). The tightest is 1. |
| 2 | 1 | cap[3][5] = 0, cap[5][3] = 1 | 1 | 1 | Push 1 | cap[0][3] drops to 3, and the undo edges 3 → 0 and 5 → 3 each gain 1. |
| 3 | 2 | 0→3→1→5 | 3 | 1 | BFS finds 3 edges | 3 → 5 is full, so BFS goes through node 1. The rooms are 3, 4, and 3, so the bottleneck is 3. |
| 4 | 2 | cap[0][3] = 0, cap[1][3] = 3 | 3 | 4 | Push 3 | 0 → 3 and 1 → 5 are now full. The undo edge 1 → 3 has 3 units of room. |
| 5 | 3 | 0→4→1→3→2→5 | 1 | 4 | Uses undo edge 1→3 | Node 4 can reach 1, but 1 → 5 is full. The undo edge 1 → 3 lets it cancel some of node 3's flow into 1, and node 3 sends that unit to 2 instead. The rooms are 4, 1, 3, 1, 2. |
| 6 | 3 | cap[1][3] = 2, cap[3][2] = 0 | 1 | 5 | Push 1 | Now 3 sends 2 units to 1 instead of 3, node 4 sends 1 unit to 1, and node 1 still sends 3 to the sink. |
| 7 | 4 | none | - | 5 | BFS reaches only 0, 4 | 0 → 3 and 4 → 1 are full, so the sink can't be reached. Return flow. |
It returns 5. The nodes BFS reached last, {0, 4}, form the minimum cut: the edges leaving them, 0 → 3 (4) and 4 → 1 (1), add up to exactly 5. Without undo edges, the method stops after round 2 at 4.
Compared with other ways
When to pick something else
| Approach | Time | Extra memory | Choose it when |
|---|---|---|---|
| Maximum flow (Edmonds-Karp) | O(n³ · e) | O(n²) | Up to a few hundred nodes, where the short matrix code is easiest to get right. |
| Edmonds-Karp with adjacency lists | O(n · e^2) | O(n + e) | Many nodes but few edges, where an n by n table would waste memory. |
| Dinic's algorithm | O(n^2 · e) | O(n + e) | Large inputs or bipartite matching, where pushing many paths per BFS pays off. |
Extra memory: O(n²): the cap table has a cell for every pair of nodes, plus parent and queue with n entries each.
How the steps grow
- Maximum flow (Edmonds-Karp)
O(n³ · e) - Edmonds-Karp with adjacency lists, Dinic's algorithm
O(n · e^2)
| Approach | n = 10 | n = 1,000 | n = 1,000,000 |
|---|---|---|---|
| Maximum flow (Edmonds-Karp) | 10,000 | 1 trillion | more than a quadrillion |
| Edmonds-Karp with adjacency lists | 1,000 | 1 billion | more than a quadrillion |
| Dinic's algorithm | 1,000 | 1 billion | more than a quadrillion |
Inputs that need care
6 tricky inputs
source === sinkparent[sink]isn't-1, the bottleneck loop never runs, andflowgrows byInfinityforever. Check for this first and return what the problem expects.- No path from source to sink
- The first BFS fails and it returns 0.
- Two edges
a → bwith capacities 2 and 3 cap[a][b] += capacityadds them to 5, which is correct: they act as one wider pipe.- Edges in both directions,
a → bandb → a - Both capacities sit in the table, and undo room is added on top. The answer is still correct.
- Capacities with decimals, like
0.1 - The loop still ends, but rounding can leave tiny leftovers like
1e-17that count as room. Scale to whole numbers, or compare with a small tolerance. - Undirected pipes
- Add each pipe as two directed edges, each with the full capacity.
Variations and follow-ups
Twists interviewers add
Finding the minimum cut
After the last BFS, parent marks the nodes still reachable from the source. The original edges from those nodes to unreached nodes are the cut: the cheapest set of edges whose removal separates source from sink.
Node limits and disjoint paths
With every capacity set to 1, the maximum flow is the number of paths from source to sink that share no edge. To limit how much passes through a node, split it into an "in" node and an "out" node joined by an edge with that limit.
Dinic's algorithm
Run one BFS to label each node's distance, then push flow along many shortest paths at once before searching again. It takes O(n² · e) in general and is much faster on matching problems.
Cheapest maximum flow
If each edge also has a cost per unit, find each augmenting path with a shortest-path search on costs, like Bellman-Ford, with undo edges getting the negative cost. The result is the maximum flow with the lowest total cost.
In an interview
Spot it, explain it, answer follow-ups
- Spot it from capacities, limits, or bandwidth plus "the most that can get through", from "match as many pairs as possible", or from "the fewest edges to cut so
acan't reachb". Saying "min cut equals max flow" shows you see the link. - Explain undo edges with a concrete reroute, like round 3 of the trace: "pushing back along
1 → 3means node 3 sends less to node 1 and sends that unit to node 2 instead". - Test by hand a graph where the first path blocks a better plan, and check that a later round uses an undo edge. Also test a graph where the sink can't be reached, which must return 0.
- Expect "how do you turn matching into flow?". Answer: a source with capacity 1 to each left item, capacity 1 from each left item to each right item it may pair with, and capacity 1 from each right item to the sink. The maximum flow is the largest number of pairs.
Where frontend code uses it
The same idea in real UI code
- Assignment features such as matching reviewers to pull requests or mentors to students, where each person can take only a limited number.
- Capacity planning dashboards that compute how much traffic can get from users to a backend through CDN nodes and links with bandwidth limits.
- Scheduling tools that fill time slots or rooms as fully as possible under per-slot and per-person limits.