Advanced 28 min readModule: Module 15: Advanced Graph Network Flows: Dinic's Algorithm
Network Flow Algorithms: Dinic's & Bipartite Matching
Solve complex network routing and assignment problems: Maximum Flow / Minimum Cut theorem, Residual graphs, Dinic's Algorithm using BFS Level Graphs and DFS Blocking Flows in O(V²E) time, and Hopcroft-Karp Bipartite Matching.
What You Will Learn in This Lesson
- The Max-Flow Min-Cut Theorem and Ford-Fulkerson foundations
- Why Edmonds-Karp is slow (O(V E²)) and how Dinic's algorithm optimizes flow to O(V² E)
- Constructing BFS Level Graphs and pushing blocking flows via DFS with pointer pruning
- Solving Maximum Bipartite Matching in O(E √V) with Hopcroft-Karp
Introduction & Core Concept
Network Flow algorithms model how resources (liquid in pipes, network packets in routers, traffic in road grids, tasks assigned to workers) can flow through a directed capacity-constrained graph from a Source (S) to a Sink (T). Dinic's algorithm is one of the most efficient Max-Flow algorithms in computer science: it builds a layered BFS Level Graph and pushes multiple Augmenting Paths simultaneously via DFS Blocking Flows.
WHY DOES THIS MATTER IN THE REAL WORLD?
Network flow algorithms power airline crew scheduling, ride-share dispatch matching (Uber/Lyft driver assignment), image segmentation in computer vision, and network routing throughput optimization.
Syntax & Structure
javascript
// Dinic's Flow Loopwhile (bfs_build_level_graph()) { while (flow = dfs_blocking_flow(s, t, INF)) max_flow += flow;}Implementing Dinic's Maximum Network Flow Algorithm
javascriptjavascript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192// Dinic's Maximum Network Flow Algorithm Implementationclass Edge {constructor(to, cap, flow = 0, revIndex = 0) {this.to = to;this.cap = cap;this.flow = flow;this.revIndex = revIndex; // Index of reverse residual edge}}class DinicMaxFlow {constructor(numVertices) {this.V = numVertices;this.adj = Array.from({ length: numVertices }, () => []);this.level = new Array(numVertices);this.ptr = new Array(numVertices); // DFS edge pointer for pruning}addEdge(from, to, capacity) {const forward = new Edge(to, capacity, 0, this.adj[to].length);const backward = new Edge(from, 0, 0, this.adj[from].length);this.adj[from].push(forward);this.adj[to].push(backward);}// Step 1: BFS to build layered level graphbfs(source, sink) {this.level.fill(-1);this.level[source] = 0;const queue = [source];while (queue.length > 0) {const u = queue.shift();for (const edge of this.adj[u]) {if (edge.cap - edge.flow > 0 && this.level[edge.to] === -1) {this.level[edge.to] = this.level[u] + 1;queue.push(edge.to);}}}return this.level[sink] !== -1;}// Step 2: DFS to push blocking flow along level graphdfs(u, sink, pushed) {if (pushed === 0 || u === sink) return pushed;for (let cid = this.ptr[u]; cid < this.adj[u].length; cid++) {this.ptr[u] = cid;const edge = this.adj[u][cid];const tr = edge.to;if (this.level[u] + 1 !== this.level[tr] || edge.cap - edge.flow === 0) continue;const pushable = Math.min(pushed, edge.cap - edge.flow);const flow = this.dfs(tr, sink, pushable);if (flow > 0) {edge.flow += flow;this.adj[tr][edge.revIndex].flow -= flow;return flow;}}return 0;}computeMaxFlow(source, sink) {let totalFlow = 0;while (this.bfs(source, sink)) {this.ptr.fill(0);while (true) {const pushed = this.dfs(source, sink, Infinity);if (pushed === 0) break;totalFlow += pushed;}}return totalFlow;}}// Verification Graph: Source (0) -> (1, 2) -> Sink (3)const dinic = new DinicMaxFlow(4);dinic.addEdge(0, 1, 10);dinic.addEdge(0, 2, 10);dinic.addEdge(1, 2, 2);dinic.addEdge(1, 3, 4);dinic.addEdge(2, 3, 9);const maxFlow = dinic.computeMaxFlow(0, 3);console.log("=== Dinic's Maximum Network Flow Algorithm ===");console.log("Computed Maximum Flow:", maxFlow); // Expected: 13console.log("✅ Optimal bottleneck capacity computed in O(V²E) time!");
Line-by-Line Technical Breakdown
1Bipartite Matching with Dinic: Any Maximum Bipartite Matching problem (e.g. matching N job applicants to M job vacancies) can be modeled as Max-Flow in O(E √V) time by creating a Source connected to all applicants with capacity 1, and connecting all jobs to a Sink with capacity 1.
Try It Yourself (Interactive Editor)
Modify the code in real-time and click Run to test live browser output and console logs.
Intelligent Code Runner & Live Sandbox[JAVASCRIPT]
JAVASCRIPT SOURCE EDITOR
Interactive Live CodeCommon Mistakes & How to Avoid Them
#1: Omitting reverse residual edges with negative flow adjustments, preventing the algorithm from routing around suboptimal paths.
Without reverse residual edges, the flow network cannot 'push back' flow if a better global route is discovered later.
Incorrect / Antipattern
// Adding only forward edge without backward edgeCorrect / Professional Solution
adj[from].push(forward); adj[to].push(backwardWithZeroCap);Industry Best Practices & Professional Standards
- Use Dinic's algorithm for general maximum flow networks.
- Use Hopcroft-Karp when specifically solving Maximum Bipartite Matching for O(E √V) performance.
- Reset edge pointers (`ptr.fill(0)`) before each DFS blocking flow phase.
Lesson Summary & Core Takeaways
- Network Flow algorithms calculate maximum capacity routing from Source to Sink.
- Dinic's algorithm combines BFS Level Graphs with DFS Blocking Flows in O(V² E).
- Max-Flow Min-Cut theorem solves network routing, matching, and image segmentation problems.