View Javadoc
1   package edu.uci.ics.jung.algorithms.shortestpath;
2   
3   import java.util.Collection;
4   import java.util.HashSet;
5   import java.util.Map;
6   import java.util.Set;
7   
8   import com.google.common.base.Function;
9   import com.google.common.base.Functions;
10  import com.google.common.base.Supplier;
11  
12  import edu.uci.ics.jung.graph.Forest;
13  import edu.uci.ics.jung.graph.Graph;
14  import edu.uci.ics.jung.graph.util.EdgeType;
15  import edu.uci.ics.jung.graph.util.Pair;
16  
17  /**
18   * For the input Graph, creates a MinimumSpanningTree
19   * using a variation of Prim's algorithm.
20   * 
21   * @author Tom Nelson - tomnelson@dev.java.net
22   *
23   * @param <V> the vertex type
24   * @param <E> the edge type
25   */
26  public class MinimumSpanningForest<V,E> {
27  	
28  	protected Graph<V,E> graph;
29  	protected Forest<V,E> forest;
30  	protected Function<E, Double> weights;
31  	
32  	/**
33  	 * Creates a Forest from the supplied Graph and supplied Supplier, which
34  	 * is used to create a new, empty Forest. If non-null, the supplied root
35  	 * will be used as the root of the tree/forest. If the supplied root is
36  	 * null, or not present in the Graph, then an arbitrary Graph vertex
37  	 * will be selected as the root.
38  	 * If the Minimum Spanning Tree does not include all vertices of the
39  	 * Graph, then a leftover vertex is selected as a root, and another
40  	 * tree is created.
41  	 * @param graph the input graph
42  	 * @param Supplier the Supplier to use to create the new forest
43  	 * @param root the vertex of the graph to be used as the root of the forest 
44  	 * @param weights edge weights
45  	 */
46  	public MinimumSpanningForest(Graph<V, E> graph, Supplier<Forest<V,E>> Supplier, 
47  			V root, Map<E, Double> weights) {
48  		this(graph, Supplier.get(), root, weights);
49  	}
50  	
51  	/**
52  	 * Creates a minimum spanning forest from the supplied graph, populating the
53  	 * supplied Forest, which must be empty. 
54  	 * If the supplied root is null, or not present in the Graph,
55  	 * then an arbitrary Graph vertex will be selected as the root.
56  	 * If the Minimum Spanning Tree does not include all vertices of the
57  	 * Graph, then a leftover vertex is selected as a root, and another
58  	 * tree is created
59  	 * @param graph the Graph to find MST in
60  	 * @param forest the Forest to populate. Must be empty
61  	 * @param root first Tree root, may be null
62  	 * @param weights edge weights, may be null
63  	 */
64  	public MinimumSpanningForest(Graph<V, E> graph, Forest<V,E> forest, 
65  			V root, Map<E, Double> weights) {
66  		
67  		if(forest.getVertexCount() != 0) {
68  			throw new IllegalArgumentException("Supplied Forest must be empty");
69  		}
70  		this.graph = graph;
71  		this.forest = forest;
72  		if(weights != null) {
73  			this.weights = Functions.forMap(weights);
74  		}
75  		Set<E> unfinishedEdges = new HashSet<E>(graph.getEdges());
76  		if(graph.getVertices().contains(root)) {
77  			this.forest.addVertex(root);
78  		}
79  		updateForest(forest.getVertices(), unfinishedEdges);
80  	}
81  	
82      /**
83       * Creates a minimum spanning forest from the supplied graph, populating the
84       * supplied Forest, which must be empty. 
85       * If the supplied root is null, or not present in the Graph,
86       * then an arbitrary Graph vertex will be selected as the root.
87       * If the Minimum Spanning Tree does not include all vertices of the
88       * Graph, then a leftover vertex is selected as a root, and another
89       * tree is created
90       * @param graph the Graph to find MST in
91       * @param forest the Forest to populate. Must be empty
92       * @param root first Tree root, may be null
93       */
94      @SuppressWarnings("unchecked")
95  	public MinimumSpanningForest(Graph<V, E> graph, Forest<V,E> forest, 
96              V root) {
97          
98          if(forest.getVertexCount() != 0) {
99              throw new IllegalArgumentException("Supplied Forest must be empty");
100         }
101         this.graph = graph;
102         this.forest = forest;
103         this.weights = (Function<E, Double>) Functions.constant(1.0);
104         Set<E> unfinishedEdges = new HashSet<E>(graph.getEdges());
105         if(graph.getVertices().contains(root)) {
106             this.forest.addVertex(root);
107         }
108         updateForest(forest.getVertices(), unfinishedEdges);
109     }
110 	
111 	/**
112 	 * @return the generated forest
113 	 */
114 	public Forest<V,E> getForest() {
115 		return forest;
116 	}
117 	
118 	protected void updateForest(Collection<V> tv, Collection<E> unfinishedEdges) {
119 		double minCost = Double.MAX_VALUE;
120 		E nextEdge = null;
121 		V nextVertex = null;
122 		V currentVertex = null;
123 		for(E e : unfinishedEdges) {
124 			
125 			if(forest.getEdges().contains(e)) continue;
126 			// find the lowest cost edge, get its opposite endpoint,
127 			// and then update forest from its Successors
128 			Pair<V> endpoints = graph.getEndpoints(e);
129 			V first = endpoints.getFirst();
130 			V second = endpoints.getSecond();
131 			if(tv.contains(first) == true && tv.contains(second) == false) {
132 				if(weights.apply(e) < minCost) {
133 					minCost = weights.apply(e);
134 					nextEdge = e;
135 					currentVertex = first;
136 					nextVertex = second;
137 				}
138 			}
139 			if(graph.getEdgeType(e) == EdgeType.UNDIRECTED &&
140 					tv.contains(second) == true && tv.contains(first) == false) {
141 				if(weights.apply(e) < minCost) {
142 					minCost = weights.apply(e);
143 					nextEdge = e;
144 					currentVertex = second;
145 					nextVertex = first;
146 				}
147 			}
148 		}
149 		
150 		if(nextVertex != null && nextEdge != null) {
151 			unfinishedEdges.remove(nextEdge);
152 			forest.addEdge(nextEdge, currentVertex, nextVertex);
153 			updateForest(forest.getVertices(), unfinishedEdges);
154 		}
155 		Collection<V> leftovers = new HashSet<V>(graph.getVertices());
156 		leftovers.removeAll(forest.getVertices());
157 		if(leftovers.size() > 0) {
158 			V anotherRoot = leftovers.iterator().next();
159 			forest.addVertex(anotherRoot);
160 			updateForest(forest.getVertices(), unfinishedEdges);
161 		}
162 	}
163 }