DSA DSA In Depth
0%
lesson bipartite-matchinghopcroft-karpkonig-theorem

Maximum Bipartite Matching: König's Theorem, Hall's Marriage Theorem & Hopcroft-Karp O(E√V)

7 min read Module 5.3: Network Flow, Min-Cut & Bipartite Matching Updated Oct 23, 2026
📄 Cheat Sheet
Hopcroft-Karp O(E√V) Maximum Bipartite Matching Algorithm Explained
Joromy Bou Khalil Hopcroft-Karp O(E√V) Maximum Bipartite Matching Algorithm Explained Watch on YouTube ↗

Explains alternating and augmenting paths in bipartite graphs, Hall's Marriage condition, and Hopcroft-Karp's simultaneous path augmentation.

0:00 / --:--

Alternating Paths, Berge's Lemma & Hopcroft-Karp $O(E \sqrt{V})$

Given a matching $M$, a vertex is matched if it is incident to an edge in $M$, and free (unmatched) otherwise. An alternating path alternates between unmatched edges ($E \setminus M$) and matched edges ($M$). An augmenting path $P$ is an alternating path that both starts at a free vertex in $L$ and ends at a free vertex in $R$. Flipping the membership of edges along $P$ ($M' = M \oplus P$) replaces $k$ matched edges with $k + 1$ unmatched edges, increasing $|M|$ by $1$!

Formal Theorems — Berge's Lemma, Hall's Marriage Theorem & König's Duality
  1. Berge's Lemma (1957): A matching $M$ is maximum if and only if $G$ contains no $M$-augmenting path.
  2. Hall's Marriage Theorem (1935): A bipartite graph $G = (L \cup R, E)$ has a matching saturating all of $L$ ($|M| = |L|$) if and only if for every subset $S \subseteq L$, its neighborhood $N(S) \subseteq R$ satisfies: $$|N(S)| \ge |S|$$
  3. König's Theorem (1931): In any bipartite graph, the maximum matching size equals the minimum vertex cover size, and the maximum independent set size is $|V| - |M^*|$: $$\nu(G) = |M^*| = \tau(G) = |C^*|, \qquad \alpha(G) = |V| - |M^*|$$

How Hopcroft-Karp Achieves $O(E \sqrt{V})$

While Kuhn's algorithm finds one augmenting path per $O(E)$ DFS ($O(V E)$ total), Hopcroft-Karp mirrors Dinic's algorithm on the bipartite graph directly:

  1. Multi-Source BFS from all free $u \in L$: Assigns distances dist[u] along alternating layers until the shortest augmenting path length $k$ to a free vertex in $R$ is reached.
  2. Simultaneous Vertex-Disjoint DFS: Finds a maximal set of vertex-disjoint shortest augmenting paths of length $k$ in $O(E)$ time and flips all of them. Because the shortest augmenting path length strictly increases each phase, at most $2\sqrt{V}$ phases occur!
flowchart LR FreeL["Seed BFS queue with all unmatched u in L (dist[u] = 0)"] --> Layer["Expand alternating paths: u --unmatched--> v --matched--> matchR[v]"] Layer --> Found{"Reached any free v in R?"} Found -- "No" --> Opt["Matching M is Maximum (Extract König Cover)"] Found -- "Yes" --> DFS["Run DFS from each free u in L along dist[next] == dist[u] + 1"] DFS --> Flip["Flip M = M (+) P along vertex-disjoint augmenting paths"] Flip --> FreeL
Dinic’s Max-Flow: BFS Level Graph, Blocking Flow, S-T Min-Cut & Hopcroft-Karp
O(V²E) Dinic · O(E√V) Unit / Matching

Visualize Dinic’s BFS level graph L_G and current-arc DFS blocking flows alongside backward residual arcs (v→u), extract the saturated S-T minimum cut, or step through Hopcroft-Karp disjoint shortest augmenting paths.

Step 1 / 8
Custom Input:
Timeline

Invariant & State

Phase 1 Blocking Flow: Pushed 4 along S→A→C→T and 10 along S→{A,B}→D→T. Edges S→A, A→C, D→T saturated! Total flow = 14.
10/10 4/10 4/4 6/8 4/9 4/10 0/6 10/10 S lvl=0 A lvl=1 B lvl=1 C lvl=2 D lvl=2 T lvl=3
Total Max-Flow |f|
14
Dinic Phase
Phase 1 Blocking Flow (level[T] = 3)
S-T Min-Cut Capacity
c(S→A)=10 + c(B→D)=9 = 19
[Step 1] Phase 1 Blocking Flow: Pushed 4 along S→A→C→T and 10 along S→{A,B}→D→T. Edges S→A, A→C, D→T saturated! Total flow = 14.
Constructive Algorithm — Extracting Minimum Vertex Cover & Maximum Independent Set

Once a maximum matching $M^*$ is computed, how do you construct the Minimum Vertex Cover $C^*$? Start an alternating reachability search from all unmatched vertices in $L$ (moving $L \to R$ via unmatched edges and $R \to L$ via matched edges). Let $Z$ be the set of visited vertices. Then the Minimum Vertex Cover is:

$$C^* = (L \setminus Z) \cup (R \cap Z)$$

And the Maximum Independent Set is its complement $I^* = V \setminus C^* = (L \cap Z) \cup (R \setminus Z)$!

AlgorithmTime ComplexitySpace ComplexityBest Use Case
Kuhn's Alternating DFS$O(V E)$$O(V + E)$Small bipartite graphs ($V \le 2000$), concise code
Hopcroft-Karp$O(E \sqrt{V})$$O(V + E)$Large bipartite graphs ($V, E \le 2 \times 10^5$)
König Cover Extraction$O(V + E)$$O(V)$Post-processing after Hopcroft-Karp

Production C++20 Implementation: Hopcroft-Karp with König Minimum Vertex Cover

#include <vector>
#include <queue>
#include <limits>

class HopcroftKarp {
public:
    HopcroftKarp(int nLeft, int nRight)
        : nL(nLeft), nR(nRight), adj(nLeft), matchL(nLeft, -1), matchR(nRight, -1), dist(nLeft) {}

    void addEdge(int u, int v) { adj[u].push_back(v); }

    int maxMatching() {
        int matching = 0;
        while (bfs()) {
            for (int u = 0; u < nL; ++u) {
                if (matchL[u] == -1 && dfs(u)) {
                    ++matching;
                }
            }
        }
        return matching;
    }

    // Returns {chosenLeftNodes, chosenRightNodes} forming a Minimum Vertex Cover
    std::pair<std::vector<int>, std::vector<int>> minVertexCover() {
        std::vector<uint8_t> visL(nL, 0), visR(nR, 0);
        std::queue<int> q;
        for (int u = 0; u < nL; ++u) {
            if (matchL[u] == -1) {
                visL[u] = 1;
                q.push(u);
            }
        }
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int v : adj[u]) {
                if (v != matchL[u] && !visR[v]) {
                    visR[v] = 1;
                    int nextU = matchR[v];
                    if (nextU != -1 && !visL[nextU]) {
                        visL[nextU] = 1;
                        q.push(nextU);
                    }
                }
            }
        }
        std::vector<int> coverL, coverR;
        for (int u = 0; u < nL; ++u) if (!visL[u]) coverL.push_back(u); // L  Z
        for (int v = 0; v < nR; ++v) if (visR[v]) coverR.push_back(v);  // R n Z
        return {coverL, coverR};
    }

private:
    static constexpr int INF = 1e9;
    int nL, nR;
    std::vector<std::vector<int>> adj;
    std::vector<int> matchL, matchR, dist;

    bool bfs() {
        std::queue<int> q;
        for (int u = 0; u < nL; ++u) {
            if (matchL[u] == -1) {
                dist[u] = 0;
                q.push(u);
            } else {
                dist[u] = INF;
            }
        }
        bool foundAugmenting = false;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int v : adj[u]) {
                int nextU = matchR[v];
                if (nextU == -1) {
                    foundAugmenting = true;
                } else if (dist[nextU] == INF) {
                    dist[nextU] = dist[u] + 1;
                    q.push(nextU);
                }
            }
        }
        return foundAugmenting;
    }

    bool dfs(int u) {
        for (int v : adj[u]) {
            int nextU = matchR[v];
            if (nextU == -1 || (dist[nextU] == dist[u] + 1 && dfs(nextU))) {
                matchL[u] = v;
                matchR[v] = u;
                return true;
            }
        }
        dist[u] = INF; // Dead-end pruning: never revisit u in this phase
        return false;
    }
};
Failure Mode — Applying König's Theorem to Non-Bipartite Graphs

On general (non-bipartite) graphs containing odd cycles, Maximum Matching is solvable in $O(E \sqrt{V})$ via Edmonds' Blossom algorithm, but Minimum Vertex Cover and Maximum Independent Set are NP-Hard! For example, on a triangle $K_3$, the maximum matching has size $1$, while the minimum vertex cover has size $2$.

Check yourself Question 1 of 4
Concept Check König's Theorem and Maximum Independent Set in Bipartite Graphs recall
Section: König's Duality

In a bipartite graph with $V = 120$ vertices where Hopcroft-Karp finds a Maximum Matching of size $|M^*| = 45$, what are the sizes of the **Minimum Vertex Cover** and the **Maximum Independent Set**?

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

Given a Directed Acyclic Graph (DAG) with $V$ vertices, how do you find the minimum number of vertex-disjoint directed paths needed to cover all $V$ vertices?

Key Takeaways

An M-augmenting path starts and ends at unmatched vertices and alternates between unmatched and matched edges; symmetric difference M (+) P increases |M| by 1.
Hopcroft-Karp finds a maximal set of shortest vertex-disjoint augmenting paths per phase, completing in at most 2*sqrt(V) phases and O(E sqrt(V)) time.
Setting dist[u] = INF when dfs(u) fails prunes dead-end vertices so each edge is inspected at most once per phase.
In bipartite graphs (and only bipartite graphs), König's Theorem guarantees |Max Matching| = |Min Vertex Cover| = |V| - |Max Independent Set|.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue