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); } } }