Files
Java/src/main/java/com/thealgorithms/graph/HopcroftKarp.java
ptzecher 657f433413 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
2025-08-14 19:42:51 +02:00

104 lines
2.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();
}
}