Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/* ==========================================
* JGraphT : a free Java graph-theory library
* ==========================================
*
* Project Info: http://jgrapht.sourceforge.net/
* Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh)
*
* (C) Copyright 2003-2012, by Barak Naveh and Contributors.
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
/* -------------------------
* HopcroftKarpBipartiteMatching.java
* -------------------------
* (C) Copyright 2012-2012, by Joris Kinable and Contributors.
*
* Original Author: Joris Kinable
* Contributor(s):
*
* Changes
* -------
* 26-Nov-2012 : Initial revision (JK);
*
*/
package org.jgrapht.alg;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.alg.interfaces.*;
import org.jgrapht.graph.*;
/**
* This class is an implementation of the Hopcroft-Karp algorithm which finds a
* maximum matching in an undirected simple bipartite graph. The algorithm runs
* in O(|E|*√|V|) time. The original algorithm is described in: Hopcroft, John
* E.; Karp, Richard M. (1973), "An n5/2 algorithm for maximum matchings in
* bipartite graphs", SIAM Journal on Computing 2 (4): 225–231,
* doi:10.1137/0202019 A coarse overview of the algorithm is given in:
* http://en.wikipedia.org/wiki/Hopcroft-Karp_algorithm Note: the behavior of
* this class is undefined when the input isn't a bipartite graph, i.e. when
* there are edges within a single partition!
*
* @author Joris Kinable
*/
public class HopcroftKarpBipartiteMatching
implements MatchingAlgorithm
{
private final UndirectedGraph graph;
private final Set partition1; //Partitions of bipartite graph
private final Set partition2;
private Set matching; //Set containing the matchings
private final Set unmatchedVertices1; //Set which contains the unmatched
//vertices in partition 1
private final Set unmatchedVertices2;
public HopcroftKarpBipartiteMatching(
UndirectedGraph graph,
Set partition1,
Set partition2)
{
this.graph = graph;
this.partition1 = partition1;
this.partition2 = partition2;
matching = new HashSet();
unmatchedVertices1 = new HashSet(partition1);
unmatchedVertices2 = new HashSet(partition2);
assert this.checkInputData();
this.maxMatching();
}
/**
* Checks whether the input data meets the requirements: simple undirected
* graph and bipartite partitions.
*/
private boolean checkInputData()
{
if (graph instanceof Multigraph) {
throw new IllegalArgumentException(
"Multi graphs are not allowed as input, only simple graphs!");
}
//Test the bipartite-ness
Set neighborsSet1 = new HashSet();
for (V v : partition1) {
neighborsSet1.addAll(Graphs.neighborListOf(graph, v));
}
if (interSectionNotEmpty(partition1, neighborsSet1)) {
throw new IllegalArgumentException(
"There are edges within partition 1, i.e. not a bipartite graph");
}
Set neighborsSet2 = new HashSet();
for (V v : partition2) {
neighborsSet2.addAll(Graphs.neighborListOf(graph, v));
}
if (interSectionNotEmpty(partition2, neighborsSet2)) {
throw new IllegalArgumentException(
"There are edges within partition 2, i.e. not a bipartite graph");
}
return true;
}
/**
* Greedily match the vertices in partition1 to the vertices in partition2.
* For each vertex in partition 1, check whether there is an edge to an
* unmatched vertex in partition 2. If so, add the edge to the matching.
*/
private void greedyMatch()
{
HashSet usedVertices = new HashSet();
for (V vertex1 : partition1) {
for (V vertex2 : Graphs.neighborListOf(graph, vertex1)) {
if (!usedVertices.contains(vertex2)) {
usedVertices.add(vertex2);
unmatchedVertices1.remove(vertex1);
unmatchedVertices2.remove(vertex2);
matching.add(graph.getEdge(vertex1, vertex2));
break;
}
}
}
}
/**
* This method is the main method of the class. First it finds a greedy
* matching. Next it tries to improve the matching by finding all the
* augmenting paths. This leads to a maximum matching.
*/
private void maxMatching()
{
this.greedyMatch();
List> augmentingPaths = this.getAugmentingPaths(); //Get a list with augmenting paths
while (!augmentingPaths.isEmpty()) {
for (
Iterator> it = augmentingPaths.iterator();
it.hasNext();)
{ //Process all augmenting paths
LinkedList augmentingPath = it.next();
unmatchedVertices1.remove(augmentingPath.getFirst());
unmatchedVertices2.remove(augmentingPath.getLast());
this.symmetricDifference(augmentingPath);
it.remove();
}
augmentingPaths.addAll(this.getAugmentingPaths()); //Check whether there are new augmenting paths available
}
}
/**
* Given are the current matching and a new augmenting path p. p.getFirst()
* and p.getLast() are newly matched vertices. This method updates the edges
* which are part of the existing matching with the new augmenting path. As
* a result, the size of the matching increases with 1.
*
* @param augmentingPath
*/
private void symmetricDifference(LinkedList augmentingPath)
{
int operation = 0;
//The augmenting path alternatingly has an edge which is not part of the
//matching, and an edge which is part of the matching. Edges which are
//already part of the matching are removed, the others are added.
while (augmentingPath.size() > 0) {
E edge =
graph.getEdge(augmentingPath.poll(), augmentingPath.peek());
if ((operation % 2) == 0) {
matching.add(edge);
} else {
matching.remove(edge);
}
operation++;
}
}
private List> getAugmentingPaths()
{
List> augmentingPaths = new ArrayList>();
//1. Build data structure
Map> layeredMap = new HashMap>();
for (V vertex : unmatchedVertices1) {
layeredMap.put(vertex, new HashSet());
}
Set oddLayer = new HashSet(unmatchedVertices1); //Layer L0 contains the unmatchedVertices1.
Set evenLayer;
Set usedVertices = new HashSet(unmatchedVertices1);
while (true) {
//Create a new even Layer A new layer can ONLY contain vertices
//which are not used in the previous layers Edges between odd and
//even layers can NOT be part of the matching
evenLayer = new HashSet();
for (V vertex : oddLayer) {
//List neighbors=this.getNeighbors(vertex);
List neighbors = Graphs.neighborListOf(graph, vertex);
for (V neighbor : neighbors) {
if (usedVertices.contains(neighbor)) {
// Vertices placed into odd-layer may not be matched by
// any other vertices except for the one we came from
continue;
} else {
evenLayer.add(neighbor);
if (!layeredMap.containsKey(neighbor)) {
layeredMap.put(neighbor, new HashSet());
}
layeredMap.get(neighbor).add(vertex);
}
}
}
usedVertices.addAll(evenLayer);
//Check whether we are finished generating layers. We are finished
//if 1. the last layer is empty or 2. if we reached free vertices
//in partition2.
if ((evenLayer.size() == 0)
|| this.interSectionNotEmpty(evenLayer, unmatchedVertices2))
{
break;
}
//Create a new odd Layer A new layer can ONLY contain vertices which
//are not used in the previous layers Edges between EVEN and ODD
//layers SHOULD be part of the matching
oddLayer = new HashSet();
for (V vertex : evenLayer) {
List neighbors = Graphs.neighborListOf(graph, vertex);
for (V neighbor : neighbors) {
if (usedVertices.contains(neighbor)
|| !matching.contains(
graph.getEdge(vertex, neighbor)))
{
continue;
} else {
oddLayer.add(neighbor);
if (!layeredMap.containsKey(neighbor)) {
layeredMap.put(neighbor, new HashSet());
}
layeredMap.get(neighbor).add(vertex);
}
}
}
usedVertices.addAll(oddLayer);
}
//Check whether there exist augmenting paths. If not, return an empty
//list. Else, we need to generate the augmenting paths which start at
//free vertices in the even layer and end at the free vertices at the
//first odd layer (L0).
if (evenLayer.size() == 0) {
return augmentingPaths;
} else {
evenLayer.retainAll(unmatchedVertices2);
}
//Finally, do a depth-first search, starting on the free vertices in the
//last even layer. Objective is to find as many vertex disjoint paths
//as possible.
for (V vertex : evenLayer) {
//Calculate an augmenting path, starting at the given vertex.
LinkedList augmentingPath = dfs(vertex, layeredMap);
//If the augmenting path exists, add it to the list of paths and
//remove the vertices from the map to enforce that the paths are
//vertex disjoint, i.e. a vertex cannot occur in more than 1 path.
if (augmentingPath != null) {
augmentingPaths.add(augmentingPath);
for (V augmentingVertex : augmentingPath) {
layeredMap.remove(augmentingVertex);
}
}
}
return augmentingPaths;
}
private LinkedList dfs(V startVertex, Map> layeredMap)
{
if (!layeredMap.containsKey(startVertex)) {
return null;
} else if (unmatchedVertices1.contains(startVertex)) {
LinkedList list = new LinkedList();
list.add(startVertex);
return list;
} else {
LinkedList partialPath = null;
for (V vertex : layeredMap.get(startVertex)) {
partialPath = dfs(vertex, layeredMap);
if (partialPath != null) {
partialPath.add(startVertex);
break;
}
}
return partialPath;
}
}
/**
* Helper method which checks whether the intersection of 2 sets is empty.
*
* @param vertexSet1
* @param vertexSet2
*
* @return true if the intersection is NOT empty.
*/
private boolean interSectionNotEmpty(Set vertexSet1, Set vertexSet2)
{
for (V vertex : vertexSet1) {
if (vertexSet2.contains(vertex)) {
return true;
}
}
return false;
}
@Override public Set getMatching()
{
return Collections.unmodifiableSet(matching);
}
}
// End HopcroftKarpBipartiteMatching.java