Hopcroft karp Algorithm implementation and tests (#6465)

* Add Hopcroft-Karp algorithm and tests

* Adding wikipedia url for Algorithm

* fixing test issues

* remove unused field flagged by PMD
This commit is contained in:
ptzecher
2025-08-14 20:42:51 +03:00
committed by GitHub
parent 57c6b033d1
commit 657f433413
2 changed files with 236 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
package com.thealgorithms.graph;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
/**
* HopcroftKarp 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 <a href="https://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm">
* Wikipedia: HopcroftKarp algorithm</a>
* @author ptzecher
*/
public class HopcroftKarp {
private final int nLeft;
private final List<List<Integer>> adj;
private final int[] pairU;
private final int[] pairV;
private final int[] dist;
public HopcroftKarp(int nLeft, int nRight, List<List<Integer>> 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<Integer> 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();
}
}