Maximum Edge-Disjoint Paths asks how many independent routes exist between two points. It is the classic interview moment where a graph question turns out to be a max-flow question in disguise — and once you see the reduction, it is almost no code.
Problem. Given a directed graph with a source S and a sink T, find the maximum number of
paths from S to T such that no two paths share an edge (they may share vertices).
Example: with edges S→A, S→B, A→C, B→D, A→D, C→T, D→T, the answer is 2 — for instance
S→A→C→T and S→B→D→T. No third edge-disjoint path fits.
The slow way first
You could try to enumerate paths and greedily pick ones that do not collide. But greedy picking is a trap: an early path can "block" a better pair, and backtracking over all path combinations explodes exponentially. We need a principled way to pack paths so they never overcommit an edge.
The question to ask: what limits how many disjoint paths can coexist? Each edge can belong to at most one path. That is exactly a capacity constraint — and capacities are the language of network flow.
The idea: capacity-1 edges, then max flow
Build a flow network on the same graph. Give every edge capacity 1. Now run any max-flow algorithm from S to T. Because each edge carries at most one unit, every unit of flow traces out a path that reuses no edge, and different units cannot overlap on an edge. So:
max flow value = maximum number of edge-disjoint S→T paths.
Max flow itself works by repeatedly finding an augmenting path — any S→T path that still has spare capacity — and pushing one unit along it. With unit capacities, each push saturates one fresh path and increments the count by one.
Walk through it
Step through the animation. First the graph appears and every edge is stamped with capacity 1. Then we find the augmenting path S→A→C→T and push a unit; those edges saturate and paths ticks to 1. We search again, avoid the saturated edges, and find the disjoint path S→B→D→T; paths becomes 2. A final search finds no path with spare capacity left, so the answer is 2.
Pseudocode
give every edge capacity 1
flow = 0
while there is an augmenting path P from S to T (spare capacity on each edge):
push 1 unit of flow along P # subtract 1 from each edge on P
flow = flow + 1 # one more edge-disjoint path
return flow # = maximum edge-disjoint pathsThe Python solution
def max_edge_disjoint_paths(graph, s, t):
# graph[u] = list of vertices u points to
cap = {} # cap[(u, v)] = 1 for every edge
for u in graph:
for v in graph[u]:
cap[(u, v)] = 1
flow = 0
while augment(cap, s, t): # find an S->T path with spare capacity
# subtract 1 along that path inside augment()
flow += 1 # one more edge-disjoint path
return flowcap[(u, v)] = 1gives every directed edge a single unit of capacity — the whole reduction lives in this line.augment(cap, s, t)searches (DFS or BFS) for anS→Tpath whose every edge still has capacity, and subtracts 1 along it; it returns whether such a path was found.- Each successful augment saturates one more edge-disjoint route, so we add 1 to
flow. - When no augmenting path remains,
flowis the max flow, which equals the maximum number of edge-disjoint paths.
Complexity
| Case | Time | Notes |
|---|---|---|
| Each augment (BFS/DFS) | O(E) (moderate) | visit each edge once |
| Number of augments | O(F) (moderate) | F = answer, bounded by edges out of S |
| Total (this reduction) | O(F · E) (moderate) | Ford-Fulkerson with unit caps |
O(V + E) (moderate)With unit capacities the flow value F is at most the out-degree of S, so the loop runs few times. Using BFS to find augmenting paths (Edmonds-Karp) gives a clean polynomial bound.
When this pattern shows up
When a problem asks for the maximum number of disjoint things — edge-disjoint paths, vertex-disjoint paths, a bipartite matching, or "how many simultaneous routes" — think max flow with unit capacities. Recognizing the reduction is the entire difficulty; the code is a stock max-flow loop.
Edge-disjoint and vertex-disjoint are different. For vertex-disjoint paths you must also cap the
vertices: split each vertex v into v_in → v_out with capacity 1 so no two paths share a vertex.
Plain edge capacities only forbid sharing edges.
Practice
After we saturate S→A→C→T, why can no later path use the edge S→A again?
1. What capacity do we assign to make max flow count edge-disjoint paths?
2. What does the final max flow value represent?
3. What does each augmenting step do?
4. To count VERTEX-disjoint paths instead, what must you add?