org.psjava.algo.graph.matching.HopcroftKarpAlgorithm Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of psjava Show documentation
Show all versions of psjava Show documentation
Problem Solving Library for Java
The newest version!
package org.psjava.algo.graph.matching;
import org.psjava.algo.graph.bfs.BFS;
import org.psjava.algo.graph.bfs.BFSVisitor;
import org.psjava.algo.graph.dfs.DFSVisitorBase;
import org.psjava.algo.graph.dfs.MultiSourceDFS;
import org.psjava.ds.Collection;
import org.psjava.ds.array.DynamicArray;
import org.psjava.ds.array.FirstInArray;
import org.psjava.ds.array.LastInArray;
import org.psjava.ds.graph.EdgeFilteredSubNewGraph;
import org.psjava.ds.graph.Graph;
import org.psjava.ds.graph.MutableDirectedGraph;
import org.psjava.ds.graph.BipartiteGraph;
import org.psjava.ds.graph.BipartiteGraphEdge;
import org.psjava.ds.graph.DirectedEdge;
import org.psjava.ds.map.MutableMap;
import org.psjava.ds.map.ValuesInMap;
import org.psjava.goods.GoodMutableMapFactory;
import org.psjava.util.DataFilter;
import org.psjava.util.FilteredIterable;
import org.psjava.util.VisitorStopper;
import org.psjava.util.ZeroTo;
/**
* O(V*root(E))
*/
public class HopcroftKarpAlgorithm {
public static MaximumBipartiteMatchingAlgorithm getInstance() {
return new MaximumBipartiteMatchingAlgorithm() {
@Override
public MaximumBipartiteMatchingResult calc(BipartiteGraph bg) {
Graph, Edge> adj = wrapAsGraph(bg);
while (true) {
Object bfsMark = new Object();
Collection> bfsFinishes = bfs(adj, bfsMark);
if (bfsFinishes.isEmpty())
break;
dfs(adj, bfsFinishes, bfsMark);
}
return createResult(adj);
}
};
}
private static enum Side {
LEFT, RIGHT
}
private static class Vertex {
V original;
Side side;
boolean free = true;
Vertex(V original, Side side) {
this.original = original;
this.side = side;
}
}
private static class EdgeStatus {
boolean inMatch = false;
Object bfsMark = null;
}
private static class Edge implements DirectedEdge> {
final Vertex from, to;
final EdgeStatus status;
Edge(Vertex from, Vertex to, EdgeStatus status) {
this.from = from;
this.to = to;
this.status = status;
}
@Override
public Vertex from() {
return from;
}
@Override
public Vertex to() {
return to;
}
}
private static Graph, Edge> wrapAsGraph(BipartiteGraph bg) {
MutableMap> vertex = GoodMutableMapFactory.getInstance().create();
for (V v : bg.getLeftVertices())
vertex.add(v, new Vertex(v, Side.LEFT));
for (V v : bg.getRightVertices())
vertex.add(v, new Vertex(v, Side.RIGHT));
MutableDirectedGraph, Edge> graph = MutableDirectedGraph.create();
for (Vertex v : ValuesInMap.get(vertex))
graph.insertVertex(v);
for (BipartiteGraphEdge e : bg.getEdges()) {
EdgeStatus status = new EdgeStatus();
graph.addEdge(new Edge(vertex.get(e.left()), vertex.get(e.right()), status));
graph.addEdge(new Edge(vertex.get(e.right()), vertex.get(e.left()), status));
}
return graph;
}
private static Collection> bfs(final Graph, Edge> adj, final Object mark) {
final DynamicArray> finishes = DynamicArray.create();
BFS.traverse(EdgeFilteredSubNewGraph.wrap(adj, new DataFilter>() {
@Override
public boolean isAccepted(Edge edge) {
// to alternate matched and non-matched edges.
if (edge.from.side == Side.LEFT)
return !edge.status.inMatch;
else
return edge.status.inMatch;
}
}), FilteredIterable.create(adj.getVertices(), new DataFilter>() {
@Override
public boolean isAccepted(Vertex v) {
return (v.side == Side.LEFT) && v.free;
}
}), new BFSVisitor, Edge>() {
int finishDepth = -1;
@Override
public void onDiscover(Vertex vertex, int depth, VisitorStopper stopper) {
if (finishDepth == -1 || depth <= finishDepth) {
if (vertex.side == Side.RIGHT && vertex.free) {
finishDepth = depth;
finishes.addToLast(vertex);
}
} else {
stopper.stop();
}
}
@Override
public void onWalk(Edge e) {
e.status.bfsMark = mark;
}
});
return finishes;
}
private static void dfs(final Graph, Edge> adj, final Collection> bfsFinishes, final Object bfsMark) {
MultiSourceDFS.traverse(EdgeFilteredSubNewGraph.wrap(adj, new DataFilter>() {
@Override
public boolean isAccepted(Edge edge) {
return edge.status.bfsMark == bfsMark; // uses only edges discovered in bfs step.
}
}), bfsFinishes, new DFSVisitorBase, Edge>() {
DynamicArray> path = DynamicArray.create();
@Override
public void onWalkDown(Edge edge) {
path.addToLast(edge);
}
@Override
public void onWalkUp(Edge downedEdge) {
path.removeLast();
}
@Override
public void onDiscovered(Vertex v, int depth, VisitorStopper stopper) {
if (wasBfsStart(v)) {
for (int index : ZeroTo.get(path.size()))
path.get(index).status.inMatch = (index % 2 == 0);
FirstInArray.getFirst(path).from.free = false;
LastInArray.getLast(path).to.free = false;
}
}
private boolean wasBfsStart(Vertex v) {
return v.side == Side.LEFT && v.free;
}
});
}
private static MaximumBipartiteMatchingResult createResult(Graph, Edge> adj) {
final MutableMap match = GoodMutableMapFactory.getInstance().create();
for (Vertex v : adj.getVertices())
for (Edge e : adj.getEdges(v))
if (e.status.inMatch)
match.add(e.from().original, e.to().original);
return new MaximumBipartiteMatchingResult() {
@Override
public V getMatchedVertex(V v) {
return match.get(v);
}
@Override
public int getMaxMatchCount() {
return match.size() / 2;
}
@Override
public boolean hasMatch(V v) {
return match.containsKey(v);
}
};
}
private HopcroftKarpAlgorithm() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy