--- /dev/null
+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);
+ }
+ }
+}