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.

Key takeaways
  • 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.
The core insight

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 G=(V,E)G = (V, E) in which every edge (u,v)∈E(u, v) \in E has a nonnegative capacity c(u,v)≥0c(u, v) \geq 0, representing the maximum rate at which material can travel along that edge. Two special vertices are distinguished: a source ss, which produces material, and a sink tt, which consumes it. By convention, if (u,v)∉E(u,v) \notin E we set c(u,v)=0c(u,v) = 0, so capacities are defined for all ordered pairs. A flow is a function f:V×V→Rf : V \times V \to \mathbb{R} satisfying three properties: 1. Capacity constraint: for every edge, 0≤f(u,v)≤c(u,v)0 \leq f(u,v) \leq c(u,v). A flow can never exceed a pipe's rated capacity, and (in the standard single-commodity model) flow is nonnegative. 2. Skew symmetry: f(u,v)=−f(v,u)f(u,v) = -f(v,u). This is a bookkeeping convention: sending 5 units from uu to vv is equivalent to sending −5-5 units from vv to uu. It makes later formulas (like net flow into a vertex) clean. 3. Flow conservation: for every vertex u∈V∖{s,t}u \in V \setminus \{s, t\},

∑v∈Vf(u,v)=0.\sum_{v \in V} f(u, v) = 0.

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 ff, denoted ∣f∣|f|, is the net flow leaving the source:

∣f∣=∑v∈Vf(s,v).|f| = \sum_{v \in V} f(s, v).

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 ff of maximum value ∣f∣|f|. 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 GfG_f. For each edge (u,v)(u,v), the residual capacity is

cf(u,v)=c(u,v)−f(u,v),c_f(u, v) = c(u,v) - f(u,v),

the amount of additional flow that could still be pushed from uu to vv. Crucially, the residual graph also contains a "back edge" (v,u)(v, u) with residual capacity cf(v,u)=f(u,v)c_f(v,u) = f(u,v), representing the ability to cancel flow already sent along (u,v)(u,v). 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 ss to tt in the residual graph GfG_f 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 ss to tt and strictly increase ∣f∣|f|.

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 GG 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 (u,v)(u,v) 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, cf(u,v)=c(u,v)−f(u,v)c_f(u,v) = c(u,v) - f(u,v).

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 cf>0c_f > 0.

Step 5 — Augment. Push the bottleneck capacity along the path, updating ff (and hence cfc_f) 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.

Figure
A small flow network with capacities, and its residual graph after routing 2 units s→a→b→t
Original network G (capacities) s a b t cap 3 cap 2 cap 4 cap 2 cap 3 Residual graph G_f after augmenting 2 units along s→a→b→t s a t 1 2 (undo) 0 (saturated, omitted) 2 (undo)

Complexity & correctness

Representing a flow and validating it takes O(V+E)O(V + E) time: checking the capacity constraint is O(1)O(1) per edge, and checking conservation requires summing incident edges once per vertex, which totals O(E)O(E) across all vertices using an adjacency list. Space is O(V+E)O(V + E) for the graph plus O(E)O(E) for the flow values (one per edge, or O(V2)O(V^2) if an adjacency-matrix representation is used).

Building the residual graph is O(E)O(E) — for every original edge we examine its current flow value and derive at most two residual entries (forward and backward), each in O(1)O(1).

Finding one augmenting path via BFS or DFS is O(V+E)O(V + E), standard for graph traversal.

Augmenting along a found path touches at most V−1V - 1 edges (a simple path has fewer than VV vertices), so it is O(V)O(V) 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 O(VE)O(VE) augmentations. What we can prove here, rigorously, are the structural facts that make the whole theory work.

Claim 1 — flow out of ss equals flow into tt. Sum the conservation equation ∑vf(u,v)=0\sum_v f(u,v) = 0 over all u∈V∖{s,t}u \in V \setminus \{s,t\}. By skew symmetry, every term f(u,v)f(u,v) for two internal vertices cancels with f(v,u)=−f(u,v)f(v,u) = -f(u,v) in the corresponding sum for vv, leaving only terms touching ss or tt:

0=∑u≠s,t∑vf(u,v)=−∑vf(s,v)−∑vf(t,v),0 = \sum_{u \neq s,t} \sum_v f(u,v) = -\sum_v f(s,v) - \sum_v f(t,v),

so ∑vf(s,v)=−∑vf(t,v)\sum_v f(s,v) = -\sum_v f(t,v), i.e. net flow out of ss equals net flow into tt. This is why ∣f∣|f| can be measured at either terminal — a useful sanity check when validating an implementation.

Claim 2 — capacity constraints bound ∣f∣|f|. For any partition of VV into a set SS containing ss and T=V∖ST = V \setminus S containing tt (a cut), one can show (by a telescoping argument identical in spirit to Claim 1, restricted to vertices inside SS) that

∣f∣=∑u∈S,v∈Tf(u,v)−∑u∈S,v∈Tf(v,u)≤∑u∈S,v∈Tc(u,v).|f| = \sum_{u \in S, v \in T} f(u,v) - \sum_{u \in S, v \in T} f(v,u) \leq \sum_{u \in S, v \in T} c(u,v).

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 cf(u,v)>0c_f(u,v) > 0, there is genuine freedom to change ff: either f(u,v)<c(u,v)f(u,v) < c(u,v) (send more forward, still legal by the capacity constraint) or f(v,u)>0f(v,u) > 0 (cancel some flow currently going the other way, which is legal since flows may be any value down to 0). Conversely, pushing δ\delta units of flow along a full augmenting path — where δ\delta is the bottleneck — preserves both the capacity constraint (since δ≤cf\delta \leq c_f on every edge of the path by definition of bottleneck) and conservation (since at every intermediate vertex, the extra δ\delta arriving via one edge is exactly matched by δ\delta leaving via the next). Hence `AUGMENT` always produces a new valid flow, and its value strictly increases by δ>0\delta > 0. 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 ∣f∣|f|.

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: s→as \to a (cap 3), s→bs \to b (cap 2), a→ba \to b (cap 4), a→ta \to t (cap 2), b→tb \to t (cap 3). We trace two augmentations by hand.

Initial state: f≡0f \equiv 0 everywhere. Residual capacities equal original capacities; no back edges are usable (all zero).

Edgecapacityflow (start)residual (start)
s→a303
s→b202
a→b404
a→t202
b→t303

Augmentation 1: BFS from ss finds path s→a→ts \to a \to t (via forward residual edges, both positive). Bottleneck =min⁡(cf(s,a),cf(a,t))=min⁡(3,2)=2= \min(c_f(s,a), c_f(a,t)) = \min(3, 2) = 2. Push 2 units.

Edgeflow after aug. 1residual after aug. 1reverse residual
s→a212
a→t202

Flow value so far: ∣f∣=2|f| = 2.

Augmentation 2: BFS from ss: s→as\to a has residual 1 (usable), a→ta \to t is now saturated (residual 0, unusable), but a→ba \to b has residual 4 and b→tb \to t has residual 3 — so the path s→a→b→ts \to a \to b \to t is found. Bottleneck =min⁡(cf(s,a),cf(a,b),cf(b,t))=min⁡(1,4,3)=1= \min(c_f(s,a), c_f(a,b), c_f(b,t)) = \min(1, 4, 3) = 1. Push 1 unit.

Edgeflow after aug. 2residual after aug. 2
s→a30
a→b13
b→t12

Flow value so far: ∣f∣=3|f| = 3.

Augmentation 3: BFS from ss: s→as \to a is now saturated (residual 0). s→bs \to b has residual 2 — usable — then b→tb \to t has residual 2. Path s→b→ts \to b \to t found. Bottleneck =min⁡(2,2)=2= \min(2, 2) = 2. Push 2 units.

Edgeflow after aug. 3residual after aug. 3
s→b20
b→t30

Flow value now: ∣f∣=5|f| = 5.

Termination check: From ss, both s→as\to a and s→bs \to b are now saturated (residual 0), and no back edges from aa or bb lead anywhere new that reaches tt (they only lead back to ss, already visited). No augmenting path exists — BFS from ss can only reach {s}\{s\} itself via forward edges, since both outgoing edges of ss are saturated. The algorithm halts with ∣f∣=5|f| = 5.

Confirming optimality via Claim 2: take the cut S={s}S = \{s\}, T={a,b,t}T = \{a, b, t\}. Its capacity is c(s,a)+c(s,b)=3+2=5c(s,a) + c(s,b) = 3 + 2 = 5, exactly matching ∣f∣=5|f| = 5. By weak duality (∣f∣≤|f| \leq 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

PitfallWhy it happensFix
Forgetting reverse (back) edges in the residual graphTreating GfG_f as just "leftover capacity" on original edgesAlways add a reverse edge with capacity f(u,v)f(u,v), even if (v,u)(v,u) wasn't in the original graph
Confusing "no augmenting path" with "flow is zero"Conflating termination condition with a degenerate caseNo augmenting path means maximum, not necessarily small — value can be large
Ignoring skew symmetry when updating flowUpdating f(u,v)f(u,v) but not f(v,u)=−f(u,v)f(v,u) = -f(u,v)Always update both directions atomically inside `AUGMENT`
Assuming any bottleneck choice gives the same number of iterationsTrue for correctness, false for efficiencyPath selection strategy (BFS vs. arbitrary DFS) changes iteration count drastically — covered under Ford–Fulkerson analysis
Applying flow conservation at ss or ttConservation is defined only for internal verticesNever assert ∑vf(s,v)=0\sum_v f(s,v) = 0; that sum is exactly ∣f∣|f|, not zero
Using integer-looking capacities but allowing fractional flow silentlyReal-valued flow libraries can drift with floating pointFor 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
LR
Lester R. Ford Jr.
1927–2017 · RAND Corporation and Bell Labs, 1950s network-flow theory

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.

History
1955
— Harris and Ross formalize a rail-network interdiction problem for RAND, an early precursor of max-flow reasoning.
1956
— Ford and Fulkerson publish the augmenting-path method and prove the max-flow min-cut theorem.
1962
— Ford and Fulkerson's Flows in Networks consolidates the theory into a unified text.
1970
— Dinic (independently, Edmonds and Karp) introduces shortest-augmenting-path strategies with proven polynomial bounds.
1988
— Goldberg and Tarjan's push-relabel method reframes flow computation via local vertex "excess," opening a new algorithmic family.

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 f(u,v)f(u,v) over all vv 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 cf(v,u)=c(v,u)−f(v,u)=c(v,u)+f(u,v)c_f(v,u) = c(v,u) - f(v,u) = c(v,u) + f(u,v) 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 ff. Concretely, for every pair (u,v)(u,v) where either an original edge or its reverse existed, the residual graph includes an edge with capacity cf(u,v)=c(u,v)−f(u,v)c_f(u,v) = c(u,v) - f(u,v), 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 f(u,v)f(u,v) 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 f(v,u)=−f(u,v)f(v,u) = -f(u,v), 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.

Before this

Where this leads