package edu.uci.ics.jung.algorithms.shortestpath;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.functors.ConstantTransformer;
import org.apache.commons.collections15.map.LazyMap;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.graph.util.Pair;
/**
* For the input Graph, creates a MinimumSpanningTree
* using a variation of Prim's algorithm.
*
* @author Tom Nelson - tomnelson@dev.java.net
*
* @param <V>
* @param <E>
*/
public class MinimumSpanningForest<V,E> {
protected Graph<V,E> graph;
protected Forest<V,E> forest;
protected Map<E,Double> weights;
/**
* Creates a Forest from the supplied Graph and supplied Factory, which
* is used to create a new, empty Forest. If non-null, the supplied root
* will be used as the root of the tree/forest. If the supplied root is
* null, or not present in the Graph, then an arbitrary Graph vertex
* will be selected as the root.
* If the Minimum Spanning Tree does not include all vertices of the
* Graph, then a leftover vertex is selected as a root, and another
* tree is created.
* @param graph the input graph
* @param factory the factory to use to create the new forest
* @param root the vertex of the graph to be used as the root of the forest
* @param weights edge weights
*/
public MinimumSpanningForest(Graph<V, E> graph, Factory<Forest<V,E>> factory,
V root, Map<E, Double> weights) {
this(graph, factory.create(), root, weights);
}
/**
* Creates a minimum spanning forest from the supplied graph, populating the
* supplied Forest, which must be empty.
* If the supplied root is null, or not present in the Graph,
* then an arbitrary Graph vertex will be selected as the root.
* If the Minimum Spanning Tree does not include all vertices of the
* Graph, then a leftover vertex is selected as a root, and another
* tree is created
* @param graph the Graph to find MST in
* @param forest the Forest to populate. Must be empty
* @param root first Tree root, may be null
* @param weights edge weights, may be null
*/
public MinimumSpanningForest(Graph<V, E> graph, Forest<V,E> forest,
V root, Map<E, Double> weights) {
if(forest.getVertexCount() != 0) {
throw new IllegalArgumentException("Supplied Forest must be empty");
}
this.graph = graph;
this.forest = forest;
if(weights != null) {
this.weights = weights;
}
Set<E> unfinishedEdges = new HashSet<E>(graph.getEdges());
if(graph.getVertices().contains(root)) {
this.forest.addVertex(root);
}
updateForest(forest.getVertices(), unfinishedEdges);
}
/**
* Creates a minimum spanning forest from the supplied graph, populating the
* supplied Forest, which must be empty.
* If the supplied root is null, or not present in the Graph,
* then an arbitrary Graph vertex will be selected as the root.
* If the Minimum Spanning Tree does not include all vertices of the
* Graph, then a leftover vertex is selected as a root, and another
* tree is created
* @param graph the Graph to find MST in
* @param forest the Forest to populate. Must be empty
* @param root first Tree root, may be null
*/
@SuppressWarnings("unchecked")
public MinimumSpanningForest(Graph<V, E> graph, Forest<V,E> forest,
V root) {
if(forest.getVertexCount() != 0) {
throw new IllegalArgumentException("Supplied Forest must be empty");
}
this.graph = graph;
this.forest = forest;
this.weights = LazyMap.decorate(new HashMap<E,Double>(),
new ConstantTransformer(1.0));
Set<E> unfinishedEdges = new HashSet<E>(graph.getEdges());
if(graph.getVertices().contains(root)) {
this.forest.addVertex(root);
}
updateForest(forest.getVertices(), unfinishedEdges);
}
/**
* Returns the generated forest.
*/
public Forest<V,E> getForest() {
return forest;
}
protected void updateForest(Collection<V> tv, Collection<E> unfinishedEdges) {
double minCost = Double.MAX_VALUE;
E nextEdge = null;
V nextVertex = null;
V currentVertex = null;
for(E e : unfinishedEdges) {
if(forest.getEdges().contains(e)) continue;
// find the lowest cost edge, get its opposite endpoint,
// and then update forest from its Successors
Pair<V> endpoints = graph.getEndpoints(e);
V first = endpoints.getFirst();
V second = endpoints.getSecond();
if(tv.contains(first) == true && tv.contains(second) == false) {
if(weights.get(e) < minCost) {
minCost = weights.get(e);
nextEdge = e;
currentVertex = first;
nextVertex = second;
}
}
if(graph.getEdgeType(e) == EdgeType.UNDIRECTED &&
tv.contains(second) == true && tv.contains(first) == false) {
if(weights.get(e) < minCost) {
minCost = weights.get(e);
nextEdge = e;
currentVertex = second;
nextVertex = first;
}
}
}
if(nextVertex != null && nextEdge != null) {
unfinishedEdges.remove(nextEdge);
forest.addEdge(nextEdge, currentVertex, nextVertex);
updateForest(forest.getVertices(), unfinishedEdges);
}
Collection<V> leftovers = new HashSet<V>(graph.getVertices());
leftovers.removeAll(forest.getVertices());
if(leftovers.size() > 0) {
V anotherRoot = leftovers.iterator().next();
forest.addVertex(anotherRoot);
updateForest(forest.getVertices(), unfinishedEdges);
}
}
}