Network Flow Fundamentals
Imagine a city water system. Pipes of fixed capacity connect a reservoir to homes. Water must obey physical laws. It cannot exceed a pipe's capacity. It cannot pool up inside a junction. How much water can flow from the reservoir to the homes per second? This deceptively simple question is the seed of one of the most powerful frameworks in algorithm design: network flow. It captures not just literal plumbing but traffic routing, airline scheduling, bipartite matching, image segmentation, and the allocation of bandwidth across the internet's backbone. The genius of the flow-network model is its generality. Once a problem is phrased as "push as much stuff as possible from a source to a sink, subject to capacity constraints," an entire toolbox of algorithms becomes available. Companies like Amazon and UPS use flow-based models to route packages through distribution hubs. Cloud providers use max-flow reasoning to provision network paths and balance load. Computer vision pipelines model foreground/background segmentation as a min-cut problem. Even matching medical residents to hospitals is a flow problem in disguise. This article lays the foundations that every flow algorithm builds on. It covers how a flow network is formally defined. It explains what the conservation and capacity constraints mean. It shows how to measure the value of a flow. Most importantly, it introduces the residual graph. This is the mechanism that lets an algorithm "undo" bad decisions. It helps the algorithm inch toward optimality. We build the vocabulary and the mental model precisely. We use worked traces and diagrams throughout. This way, later, more advanced methods feel like natural refinements, not magic tricks. By the end, you will be able to construct a flow network from a real scenario. You will be able to verify whether a proposed flow is feasible. You will be able to compute residual capacities by hand. You will understand exactly why augmenting paths are the mechanism that improves a flow. This groundwork is the direct on-ramp to the Ford-Fulkerson Method and the Max-Flow Min-Cut Theorem, covered next.
- A flow network is a directed, capacity-weighted graph with a designated source and sink.
- A valid flow must respect two rules: capacity constraints (flow ≤ capacity on every edge) and conservation (inflow = outflow at every internal vertex).
- The value of a flow is the net flow leaving the source (equivalently, entering the sink).
- The residual graph encodes how much more flow can be pushed, and how much can be undone, along every edge.
- Augmenting paths in the residual graph are the mechanism by which a flow is improved toward optimality.
- These ideas are the mathematical bedrock for Ford-Fulkerson Method and the Max-Flow Min-Cut Theorem.
A flow network turns "how much can move from A to B" into a precise optimization problem governed by two local rules — capacity and conservation — whose global consequences are captured by the residual graph.
The idea
A flow network is a directed graph in which every edge has a nonnegative capacity , representing the maximum rate at which material can travel along that edge. Two special vertices are distinguished: a source , which produces material, and a sink , which consumes it. By convention, if we set , so capacities are defined for all ordered pairs. A flow is a function satisfying three properties: 1. Capacity constraint: for every edge, . A flow can never exceed a pipe's rated capacity, and (in the standard single-commodity model) flow is nonnegative. 2. Skew symmetry: . This is a bookkeeping convention: sending 5 units from to is equivalent to sending units from to . It makes later formulas (like net flow into a vertex) clean. 3. Flow conservation: for every vertex ,
In words: at every vertex other than the source and sink, total flow in equals total flow out — material neither accumulates nor is created out of thin air at intermediate junctions. The value of a flow , denoted , is the net flow leaving the source:
A standard exercise (proved below) shows this equals the net flow arriving at the sink, so "how much leaves the source" and "how much arrives at the sink" are always the same number — flow doesn't leak or vanish anywhere in between. The central computational question this article sets up — and which Ford-Fulkerson Method answers algorithmically — is the maximum-flow problem: find a flow of maximum value . To solve it, we need a way to detect when a flow can still be improved, and that requires a second graph built from the current flow: the residual graph . For each edge , the residual capacity is
the amount of additional flow that could still be pushed from to . Crucially, the residual graph also contains a "back edge" with residual capacity , representing the ability to cancel flow already sent along . This is what lets an algorithm correct earlier suboptimal routing decisions without ever explicitly reasoning about the past — it is the single most important idea in this article, and the reason simple greedy strategies (which lack this undo mechanism) can get stuck at suboptimal flows. An augmenting path is a simple path from to in the residual graph using only edges of strictly positive residual capacity. If one exists, the flow is not yet maximum, because we can push additional units — equal to the minimum residual capacity along the path, called the bottleneck capacity — from to and strictly increase .
How it works
Before any augmenting-path search can run, we must be able to (1) validate that a candidate assignment is a legitimate flow, (2) compute the value of a flow, and (3) construct the residual graph from a flow. These three primitives are the mechanical heart of every max-flow algorithm; understanding them precisely is what this article delivers.
Step 1 — Represent the network. Store as an adjacency list (see Graph Representations: Adjacency Lists and Matrices), annotating each edge with its capacity. It is convenient to also store, for every edge present in the input, an explicit reverse entry with capacity 0, so the residual graph never needs to allocate new edges — only update capacities.
Step 2 — Validate a flow. Check capacity constraints on every edge and conservation at every non-terminal vertex.
Step 3 — Compute residual capacities. For every pair with an edge in either direction, .
Step 4 — Find an augmenting path (if one exists). Run a graph search (BFS or DFS — the choice affects later complexity guarantees, discussed in the max-flow article) restricted to edges with .
Step 5 — Augment. Push the bottleneck capacity along the path, updating (and hence ) on every edge and its reverse. Here is pseudocode for the core primitives — building the residual graph and performing one augmentation step — which together form the inner loop that later algorithms iterate to convergence. ``` // Graph G given as capacities c[u][v] for all (u,v) with an edge, // and c[u][v] = 0 if no edge (u,v) exists. // Flow f[u][v] initialized to 0 for all pairs; f[u][v] = -f[v][u]. function RESIDUAL-CAPACITY(u, v): return c[u][v] - f[u][v] function BUILD-RESIDUAL-GRAPH(G, f): G_f = empty graph on same vertex set as G for each pair (u, v) such that c[u][v] > 0 or c[v][u] > 0: r = RESIDUAL-CAPACITY(u, v) if r > 0: add edge (u, v) to G_f with capacity r return G_f function FIND-AUGMENTING-PATH(G_f, s, t): // Any graph search restricted to positive-capacity edges works; // BFS here (as in Edmonds-Karp) finds a SHORTEST augmenting path. parent = array indexed by vertex, initialized to NIL visited = {s} queue = [s] while queue is not empty: u = queue.dequeue() for each edge (u, v) in G_f with capacity c_f(u, v) > 0: if v not in visited: visited.add(v) parent[v] = u if v == t: return RECONSTRUCT-PATH(parent, s, t) queue.enqueue(v) return NIL // no augmenting path exists function AUGMENT(f, path): // path is a list of vertices s = v0, v1, ..., vk = t bottleneck = +infinity for i = 0 to k - 1: bottleneck = min(bottleneck, RESIDUAL-CAPACITY(path[i], path[i+1])) for i = 0 to k - 1: u, v = path[i], path[i+1] f[u][v] = f[u][v] + bottleneck f[v][u] = -f[u][v] // maintain skew symmetry return bottleneck function COMPUTE-FLOW-VALUE(f, s): total = 0 for each vertex v: total = total + f[s][v] return total ``` Repeatedly calling `FIND-AUGMENTING-PATH` followed by `AUGMENT` until no augmenting path remains is precisely the augmenting-path method — the general strategy that Ford-Fulkerson Method instantiates and analyzes for correctness and running time. This article's job is to make sure every ingredient in that loop — flow, capacity, conservation, residual capacity, bottleneck — is airtight before that story continues.
Complexity & correctness
Representing a flow and validating it takes time: checking the capacity constraint is per edge, and checking conservation requires summing incident edges once per vertex, which totals across all vertices using an adjacency list. Space is for the graph plus for the flow values (one per edge, or if an adjacency-matrix representation is used).
Building the residual graph is — for every original edge we examine its current flow value and derive at most two residual entries (forward and backward), each in .
Finding one augmenting path via BFS or DFS is , standard for graph traversal.
Augmenting along a found path touches at most edges (a simple path has fewer than vertices), so it is per augmentation. Honesty about scope: this article only analyzes the primitives. The number of augmentations needed to reach a maximum flow, and whether that number is polynomial, is precisely the subject of Ford-Fulkerson Method — with badly chosen augmenting paths and irrational capacities, the method can in principle fail to terminate, while BFS-based selection (Edmonds–Karp) guarantees termination in augmentations. What we can prove here, rigorously, are the structural facts that make the whole theory work.
Claim 1 — flow out of equals flow into . Sum the conservation equation over all . By skew symmetry, every term for two internal vertices cancels with in the corresponding sum for , leaving only terms touching or :
so , i.e. net flow out of equals net flow into . This is why can be measured at either terminal — a useful sanity check when validating an implementation.
Claim 2 — capacity constraints bound . For any partition of into a set containing and containing (a cut), one can show (by a telescoping argument identical in spirit to Claim 1, restricted to vertices inside ) that
So the value of any flow is at most the capacity of any cut. This inequality — weak duality between flows and cuts — is the seed from which the Max-Flow Min-Cut Theorem grows, and it already tells us something concrete now: once we exhibit a flow and a cut whose capacities match, we know instantly, without further search, that the flow is maximum.
Claim 3 — the residual graph correctly represents "room to improve." If , there is genuine freedom to change : either (send more forward, still legal by the capacity constraint) or (cancel some flow currently going the other way, which is legal since flows may be any value down to 0). Conversely, pushing units of flow along a full augmenting path — where is the bottleneck — preserves both the capacity constraint (since on every edge of the path by definition of bottleneck) and conservation (since at every intermediate vertex, the extra arriving via one edge is exactly matched by leaving via the next). Hence `AUGMENT` always produces a new valid flow, and its value strictly increases by . This is the correctness core: an augmenting path is not just a heuristic signal but a certificate that improvement is possible, and the augmentation step is guaranteed to preserve feasibility while strictly growing .
Tradeoff to flag honestly: none of this yet proves that when no augmenting path exists, the flow is maximum (that is exactly the min-cut theorem, proved via Claim 2's cut argument in the sequel). What is established here is the machinery — feasibility, residual capacity, bottleneck augmentation — on which that later proof entirely depends.
Worked example
Consider the network from the diagram: (cap 3), (cap 2), (cap 4), (cap 2), (cap 3). We trace two augmentations by hand.
Initial state: everywhere. Residual capacities equal original capacities; no back edges are usable (all zero).
| Edge | capacity | flow (start) | residual (start) |
|---|---|---|---|
| s→a | 3 | 0 | 3 |
| s→b | 2 | 0 | 2 |
| a→b | 4 | 0 | 4 |
| a→t | 2 | 0 | 2 |
| b→t | 3 | 0 | 3 |
Augmentation 1: BFS from finds path (via forward residual edges, both positive). Bottleneck . Push 2 units.
| Edge | flow after aug. 1 | residual after aug. 1 | reverse residual |
|---|---|---|---|
| s→a | 2 | 1 | 2 |
| a→t | 2 | 0 | 2 |
Flow value so far: .
Augmentation 2: BFS from : has residual 1 (usable), is now saturated (residual 0, unusable), but has residual 4 and has residual 3 — so the path is found. Bottleneck . Push 1 unit.
| Edge | flow after aug. 2 | residual after aug. 2 |
|---|---|---|
| s→a | 3 | 0 |
| a→b | 1 | 3 |
| b→t | 1 | 2 |
Flow value so far: .
Augmentation 3: BFS from : is now saturated (residual 0). has residual 2 — usable — then has residual 2. Path found. Bottleneck . Push 2 units.
| Edge | flow after aug. 3 | residual after aug. 3 |
|---|---|---|
| s→b | 2 | 0 |
| b→t | 3 | 0 |
Flow value now: .
Termination check: From , both and are now saturated (residual 0), and no back edges from or lead anywhere new that reaches (they only lead back to , already visited). No augmenting path exists — BFS from can only reach itself via forward edges, since both outgoing edges of are saturated. The algorithm halts with .
Confirming optimality via Claim 2: take the cut , . Its capacity is , exactly matching . By weak duality ( capacity of any cut), no flow can exceed 5 — so this flow is provably maximum, without needing the full min-cut theorem machinery, just Claim 2 applied to one well-chosen cut.
Common pitfalls & traps
| Pitfall | Why it happens | Fix |
|---|---|---|
| Forgetting reverse (back) edges in the residual graph | Treating as just "leftover capacity" on original edges | Always add a reverse edge with capacity , even if wasn't in the original graph |
| Confusing "no augmenting path" with "flow is zero" | Conflating termination condition with a degenerate case | No augmenting path means maximum, not necessarily small — value can be large |
| Ignoring skew symmetry when updating flow | Updating but not | Always update both directions atomically inside `AUGMENT` |
| Assuming any bottleneck choice gives the same number of iterations | True for correctness, false for efficiency | Path selection strategy (BFS vs. arbitrary DFS) changes iteration count drastically — covered under Ford–Fulkerson analysis |
| Applying flow conservation at or | Conservation is defined only for internal vertices | Never assert ; that sum is exactly , not zero |
| Using integer-looking capacities but allowing fractional flow silently | Real-valued flow libraries can drift with floating point | For integral capacities, argue integrality of an optimal flow explicitly rather than trusting floats |
Pioneers & history
The theory of network flows was born from very concrete Cold War logistics questions: how much traffic can move through a rail or highway network, and how would an adversary sabotage it most efficiently by removing the fewest links? These questions, posed to RAND Corporation researchers in the 1950s, led directly to the formalization of flow networks and cuts as dual objects — the same duality this article's Claim 2 previews.
Origins · 1 minLester R. Ford Jr. and how this was discovered
Ford, together with Delbert Fulkerson, formalized the max-flow problem and devised the augmenting-path method as an algorithmic answer to the RAND transportation-network studies. Their 1956 paper and 1962 book Flows in Networks established the vocabulary — flow, capacity, cut, augmenting path — used verbatim to this day, and proved the max-flow min-cut duality that later became a cornerstone of combinatorial optimization and linear programming duality more broadly.
Where this matters
Flow networks are not a theoretical curiosity confined to textbooks — they are load-bearing infrastructure in real systems. Internet traffic engineering models link capacities and routes packets to maximize throughput, directly using max-flow and min-cut reasoning to detect bottleneck links. Bipartite matching — assigning workers to tasks, students to schools, or organ donors to recipients — reduces exactly to a flow problem with unit capacities, letting hospitals and matching markets compute optimal, conflict-free assignments efficiently. Image segmentation in computer vision (the classic "GrabCut" style algorithms) casts foreground/background separation as a min-cut on a grid graph where edge weights encode pixel similarity. Airlines use flow models for crew scheduling and aircraft routing, where capacities represent seat or crew limits and conservation ensures every leg is properly staffed. Distribution and logistics companies solve multi-commodity flow variants — a generalization of the single-commodity model here — to route packages through warehouses under road and vehicle capacity limits. Even the theoretical computer science of project scheduling and bipartite covering problems, and reductions used in NP-hardness proofs (flow-based gadgets appear throughout complexity theory), lean on the exact definitions of flow, cut, and residual graph developed in this article. Every one of these applications ultimately runs an augmenting-path or push-relabel algorithm underneath — meaning the residual-graph mechanics taught here are the literal inner loop executing, millions of times a second, inside production routing and scheduling systems.
Ask the tutor: How does the choice of BFS versus DFS for finding augmenting paths affect the total number of iterations Ford-Fulkerson needs to converge?
Why do we need skew symmetry, f(u,v) = -f(v,u), in the definition of a flow?
Skew symmetry is bookkeeping that makes later formulas clean and consistent. It lets us treat "5 units forward on (u,v)" and "−5 units forward on (v,u)" as the same physical fact, so summing over all at a vertex correctly captures net flow without needing separate in-flow and out-flow variables. It also directly enables the residual-graph construction: the reverse residual capacity naturally represents "how much flow can be canceled," which is exactly what an augmenting algorithm needs to correct earlier suboptimal routing.
Is the residual graph a completely new graph, or built from the original?
It shares the same vertex set as the original graph, but its edge set and capacities are derived fresh from the current flow . Concretely, for every pair where either an original edge or its reverse existed, the residual graph includes an edge with capacity , provided this value is positive. Practically, implementations don't rebuild it from scratch each time; they maintain the same adjacency structure and simply update stored capacities in place as flow changes, which is more efficient than reconstructing the graph after every augmentation.
Can a flow network have negative capacities or negative flow values?
Capacities are always required to be nonnegative, since a negative capacity would mean a pipe that somehow removes the ability to send anything — physically meaningless. Flow values on an edge are also required to be nonnegative and bounded above by capacity; the appearance of "negative flow" only arises via the skew-symmetry convention , which is a notational device for the reverse direction, not a genuinely negative physical quantity flowing anywhere.
Does the augmenting-path method always terminate?
With integer capacities, yes: every augmentation strictly increases the flow value by at least 1 (the bottleneck is an integer, since it's a min of integers), and the flow value is bounded above by any cut's capacity, so only finitely many augmentations can occur. With irrational real-valued capacities and adversarially chosen augmenting paths, the classical augmenting-path method can fail to terminate or converge to a suboptimal limit — a subtlety resolved by the specific path-selection strategies analyzed in Ford-Fulkerson Method and its refinements.
How is the max-flow problem related to bipartite matching?
Any bipartite graph can be turned into a flow network: add a source connected to every vertex on one side, a sink connected from every vertex on the other side, direct the bipartite edges across, and set every capacity to 1. A maximum flow in this network corresponds exactly to a maximum matching, because unit capacities force each vertex to be used by at most one unit of flow — mirroring the "matched at most once" constraint. This reduction is why augmenting-path ideas historically cross-pollinated between flow theory and matching theory.
What's the difference between a flow network and a general weighted graph?
A general weighted graph (as in Graph Representations: Adjacency Lists and Matrices) simply assigns numbers to edges, often interpreted as cost or distance for shortest-path problems. A flow network reinterprets those numbers as capacities — upper bounds on a quantity that must also satisfy conservation at internal vertices, which shortest-path graphs never require. The extra structural constraint (conservation) is what makes flow problems a genuinely different computational animal from shortest-path or minimum-spanning-tree problems, even though all three use the same underlying graph representations.
Why is the cut-capacity bound (Claim 2) useful even before proving max-flow min-cut fully?
It gives an immediate, checkable certificate of optimality without needing the full equivalence theorem. If you exhibit a flow and a cut whose values match exactly, weak duality alone guarantees no better flow exists — you don't need to search further or trust an implementation blindly. This "produce a matching pair" style of proof is a recurring pattern in optimization broadly, and getting comfortable with it here previews exactly how the Max-Flow Min-Cut Theorem is proved in full generality.