blob: 805c7ff165ebaef58286161e70f0284cf2f5edf6 [file] [log] [blame]
package org.apache.commons.graph.spanning;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import static org.apache.commons.graph.utils.Assertions.checkNotNull;
import static org.apache.commons.graph.utils.Assertions.checkState;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.apache.commons.graph.Graph;
import org.apache.commons.graph.Mapper;
import org.apache.commons.graph.SpanningTree;
import org.apache.commons.graph.VertexPair;
import org.apache.commons.graph.collections.DisjointSet;
import org.apache.commons.graph.collections.FibonacciHeap;
import org.apache.commons.graph.math.monoid.OrderedMonoid;
import org.apache.commons.graph.model.MutableSpanningTree;
/**
* {@link SpanningTreeAlgorithmSelector} implementation.
*
* @param <V> the Graph vertices type
* @param <W> the weight type
* @param <WE> the Graph weighted edges type
*/
final class DefaultSpanningTreeAlgorithmSelector<V, W, WE>
implements SpanningTreeAlgorithmSelector<V, W, WE>
{
/** The graph. */
private final Graph<V, WE> graph;
private final Mapper<WE, W> weightedEdges;
/** The start vertex. */
private final V source;
/**
* Creates a default {@link SpanningTreeAlgorithmSelector} for the given {@link Graph} and
* start vertex.
*
* @param graph the {@link Graph} to be used.
* @param source the start vertex.
*/
public DefaultSpanningTreeAlgorithmSelector( final Graph<V, WE> graph, Mapper<WE, W> weightedEdges, final V source )
{
this.graph = graph;
this.weightedEdges = weightedEdges;
this.source = source;
}
/** {@inheritDoc} */
public <WO extends OrderedMonoid<W>> SpanningTree<V, WE, W> applyingBoruvkaAlgorithm( WO weightOperations )
{
/*
* <pre>
* procedure Boruvka MST(G(V; E)):
* T <= V
* while |T| < n do
* for all connected component C in T do
* e <= the smallest-weight edge from C to another component in T
* if e not exists in T then
* T <= T u {e}
* end if
* end for
* end while
* <pre>
*/
checkNotNull( weightOperations, "The Boruvka algorithm cannot be calculated with null weight operations" );
final MutableSpanningTree<V, WE, W> spanningTree = new MutableSpanningTree<V, WE, W>( weightOperations, weightedEdges );
final Set<SuperVertex<V, W, WE>> components = new HashSet<SuperVertex<V, W, WE>>( graph.getOrder() );
final Map<V, SuperVertex<V, W, WE>> mapping = new HashMap<V, SuperVertex<V, W, WE>>( graph.getOrder() );
for ( V v : graph.getVertices() )
{
// create a super vertex for each vertex
final SuperVertex<V, W, WE> sv = new SuperVertex<V, W, WE>( v, graph, new WeightedEdgesComparator<W, WE>( weightOperations, weightedEdges ) );
components.add( sv );
// add a mapping for each vertex to its corresponding super vertex
mapping.put( v, sv );
// add each vertex to the spanning tree
spanningTree.addVertex( v );
}
while ( components.size() > 1 )
{
final List<WE> edges = new LinkedList<WE>();
for ( SuperVertex<V, W, WE> sv : components )
{
// get the minimum edge for each component to any other component
final WE edge = sv.getMinimumWeightEdge();
if ( edge != null )
{
edges.add( edge );
}
}
// if there is no edge anymore for a component, and there is still more than 1 component,
// the graph is unconnected
checkState( !edges.isEmpty() || components.size() == 1, "unconnected graph" );
for ( final WE edge : edges )
{
final VertexPair<V> pair = graph.getVertices( edge );
final V head = pair.getHead();
final V tail = pair.getTail();
// find the super vertices corresponding to this edge
final SuperVertex<V, W, WE> headSv = mapping.get( head );
final SuperVertex<V, W, WE> tailSv = mapping.get( tail );
// merge them, if they are not the same
if ( headSv != tailSv )
{
headSv.merge( tailSv );
// update the mapping for each merged vertex
for ( final V v : tailSv )
{
mapping.put( v, headSv );
}
// remove the merged super vertex from the components set
components.remove( tailSv );
// add the edge to the spanning tree
if ( spanningTree.getVertices( edge ) == null )
{
spanningTree.addEdge( head, edge, tail );
}
}
}
}
return spanningTree;
}
/**
* {@inheritDoc}
*/
public <WO extends OrderedMonoid<W>> SpanningTree<V, WE, W> applyingKruskalAlgorithm( WO weightOperations )
{
checkNotNull( weightOperations, "The Kruskal algorithm cannot be calculated with null weight operations" );
final Set<V> settledNodes = new HashSet<V>();
final Queue<WE> orderedEdges =
new FibonacciHeap<WE>( new WeightedEdgesComparator<W, WE>( weightOperations, weightedEdges ) );
for ( WE edge : graph.getEdges() )
{
orderedEdges.add( edge );
}
final DisjointSet<V> disjointSet = new DisjointSet<V>();
final MutableSpanningTree<V, WE, W> spanningTree = new MutableSpanningTree<V, WE, W>( weightOperations, weightedEdges );
// fill the spanning tree with vertices.
for ( V v : graph.getVertices() )
{
spanningTree.addVertex( v );
}
while ( !orderedEdges.isEmpty() && settledNodes.size() < graph.getOrder() )
{
WE edge = orderedEdges.remove();
VertexPair<V> vertices = graph.getVertices( edge );
V head = vertices.getHead();
V tail = vertices.getTail();
settledNodes.add( head );
settledNodes.add( tail );
if ( !disjointSet.find( head ).equals( disjointSet.find( tail ) ) )
{
disjointSet.union( head, tail );
spanningTree.addEdge( head, edge, tail );
}
}
return spanningTree;
}
/**
* {@inheritDoc}
*/
public <WO extends OrderedMonoid<W>> SpanningTree<V, WE, W> applyingPrimAlgorithm( WO weightOperations )
{
checkNotNull( weightOperations, "The Prim algorithm cannot be calculated with null weight operations" );
final ShortestEdges<V, WE, W> shortestEdges = new ShortestEdges<V, WE, W>( graph, source, weightOperations, weightedEdges );
final Queue<V> unsettledNodes = new FibonacciHeap<V>( shortestEdges );
unsettledNodes.add( source );
final Set<WE> settledEdges = new HashSet<WE>();
// extract the node with the shortest distance
while ( !unsettledNodes.isEmpty() )
{
V vertex = unsettledNodes.remove();
for ( V v : graph.getConnectedVertices( vertex ) )
{
WE edge = graph.getEdge( vertex, v );
// if the edge has not been already visited and its weight is
// less then the current Vertex weight
boolean weightLessThanCurrent =
!shortestEdges.hasWeight( v )
|| weightOperations.compare( weightedEdges.map( edge ), shortestEdges.getWeight( v ) ) < 0;
if ( settledEdges.add( edge ) && weightLessThanCurrent )
{
if ( !unsettledNodes.contains( v ) )
{
unsettledNodes.add( v );
}
shortestEdges.addPredecessor( v, edge );
}
}
}
return shortestEdges.createSpanningTree();
}
}