package com.thealgorithms.graph; import java.util.ArrayDeque; import java.util.Arrays; import java.util.List; import java.util.Queue; /** * Hopcroft–Karp algorithm for maximum bipartite matching. * * Left part: vertices [0,nLeft-1], Right part: [0,nRight-1]. * Adjacency list: for each left vertex u, list right vertices v it connects to. * * Time complexity: O(E * sqrt(V)). * * @see * Wikipedia: Hopcroft–Karp algorithm * @author ptzecher */ public class HopcroftKarp { private final int nLeft; private final List> adj; private final int[] pairU; private final int[] pairV; private final int[] dist; public HopcroftKarp(int nLeft, int nRight, List> adj) { this.nLeft = nLeft; this.adj = adj; this.pairU = new int[nLeft]; this.pairV = new int[nRight]; this.dist = new int[nLeft]; Arrays.fill(pairU, -1); Arrays.fill(pairV, -1); } /** Returns the size of the maximum matching. */ public int maxMatching() { int matching = 0; while (bfs()) { for (int u = 0; u < nLeft; u++) { if (pairU[u] == -1 && dfs(u)) { matching++; } } } return matching; } // BFS to build layers private boolean bfs() { Queue queue = new ArrayDeque<>(); Arrays.fill(dist, -1); for (int u = 0; u < nLeft; u++) { if (pairU[u] == -1) { dist[u] = 0; queue.add(u); } } boolean foundAugPath = false; while (!queue.isEmpty()) { int u = queue.poll(); for (int v : adj.get(u)) { int matchedLeft = pairV[v]; if (matchedLeft == -1) { foundAugPath = true; } else if (dist[matchedLeft] == -1) { dist[matchedLeft] = dist[u] + 1; queue.add(matchedLeft); } } } return foundAugPath; } // DFS to find augmenting paths within the BFS layering private boolean dfs(int u) { for (int v : adj.get(u)) { int matchedLeft = pairV[v]; if (matchedLeft == -1 || (dist[matchedLeft] == dist[u] + 1 && dfs(matchedLeft))) { pairU[u] = v; pairV[v] = u; return true; } } dist[u] = -1; return false; } public int[] getLeftMatches() { return pairU.clone(); } public int[] getRightMatches() { return pairV.clone(); } }