org.jgrapht.alg.matching.GreedyMaximumCardinalityMatching Maven / Gradle / Ivy
/*
* (C) Copyright 2017-2021, by Joris Kinable and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht.alg.matching;
import org.jgrapht.*;
import org.jgrapht.alg.interfaces.*;
import java.util.*;
/**
* The greedy algorithm for computing a maximum cardinality matching. The algorithm can run in two
* modes: sorted or unsorted. When unsorted, the matching is obtained by iterating through the edges
* and adding an edge if it doesn't conflict with the edges already in the matching. When sorted,
* the edges are first sorted by the sum of degrees of their endpoints. After that, the algorithm
* proceeds in the same manner. Running this algorithm in sorted mode can sometimes produce better
* results, albeit at the cost of some additional computational overhead.
*
* Independent of the mode, the resulting matching is maximal, and is therefore guaranteed to
* contain at least half of the edges that a maximum cardinality matching has ($\frac{1}{2}$
* approximation). Runtime complexity: $O(m)$ when the edges are not sorted, $O(m + m \log n)$
* otherwise, where $n$ is the number of vertices, and $m$ the number of edges.
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Joris Kinable
*/
public class GreedyMaximumCardinalityMatching
implements
MatchingAlgorithm
{
private final Graph graph;
private final boolean sort;
/**
* Creates a new GreedyMaximumCardinalityMatching instance.
*
* @param graph graph
* @param sort sort the edges prior to starting the greedy algorithm
*/
public GreedyMaximumCardinalityMatching(Graph graph, boolean sort)
{
this.graph = GraphTests.requireUndirected(graph);
this.sort = sort;
}
/**
* Get a matching that is a $\frac{1}{2}$-approximation of the maximum cardinality matching.
*
* @return a matching
*/
@Override
public Matching getMatching()
{
Set matched = new HashSet<>();
Set edges = new LinkedHashSet<>();
double cost = 0;
if (sort) {
// sort edges in increasing order of the total degree of their endpoints
List allEdges = new ArrayList<>(graph.edgeSet());
allEdges.sort(new EdgeDegreeComparator());
for (E e : allEdges) {
V v = graph.getEdgeSource(e);
V w = graph.getEdgeTarget(e);
if (!v.equals(w) && !matched.contains(v) && !matched.contains(w)) {
edges.add(e);
matched.add(v);
matched.add(w);
cost += graph.getEdgeWeight(e);
}
}
} else {
for (V v : graph.vertexSet()) {
if (matched.contains(v))
continue;
for (E e : graph.edgesOf(v)) {
V w = Graphs.getOppositeVertex(graph, e, v);
if (!v.equals(w) && !matched.contains(w)) {
edges.add(e);
matched.add(v);
matched.add(w);
cost += graph.getEdgeWeight(e);
break;
}
}
}
}
return new MatchingImpl<>(graph, edges, cost);
}
private class EdgeDegreeComparator
implements
Comparator
{
@Override
public int compare(E e1, E e2)
{
int degreeE1 =
graph.degreeOf(graph.getEdgeSource(e1)) + graph.degreeOf(graph.getEdgeTarget(e1));
int degreeE2 =
graph.degreeOf(graph.getEdgeSource(e2)) + graph.degreeOf(graph.getEdgeTarget(e2));
return Integer.compare(degreeE1, degreeE2);
}
}
}