View Javadoc
1   /*
2   * Copyright (c) 2003, The JUNG Authors 
3   *
4   * All rights reserved.
5   *
6   * This software is open-source under the BSD license; see either
7   * "license.txt" or
8   * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9   */
10  package edu.uci.ics.jung.algorithms.shortestpath;
11  
12  import java.util.HashMap;
13  import java.util.HashSet;
14  import java.util.LinkedHashMap;
15  import java.util.LinkedList;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Set;
19  
20  import com.google.common.base.Function;
21  
22  import edu.uci.ics.jung.graph.Graph;
23  
24  /**
25   * <p>Calculates distances and shortest paths using Dijkstra's   
26   * single-source-shortest-path algorithm.  This is a lightweight
27   * extension of <code>DijkstraDistance</code> that also stores
28   * path information, so that the shortest paths can be reconstructed.
29   * 
30   * <p> The elements in the maps returned by 
31   * <code>getIncomingEdgeMap</code> are ordered (that is, returned 
32   * by the iterator) by nondecreasing distance from <code>source</code>.
33   * 
34   * @author Joshua O'Madadhain
35   * @author Tom Nelson converted to jung2
36   * @see DijkstraDistance
37   */
38  public class DijkstraShortestPath<V,E> extends DijkstraDistance<V,E> implements ShortestPath<V,E>
39  {
40      /**
41       * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
42       * the specified graph and the specified method of extracting weights 
43       * from edges, which caches results locally if and only if 
44       * <code>cached</code> is <code>true</code>.
45       * 
46       * @param g     the graph on which distances will be calculated
47       * @param nev   the class responsible for returning weights for edges
48       * @param cached    specifies whether the results are to be cached
49       */
50      public DijkstraShortestPath(Graph<V,E> g, Function<E, ? extends Number> nev, boolean cached)
51      {
52          super(g, nev, cached);
53      }
54      
55      /**
56       * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
57       * the specified graph and the specified method of extracting weights 
58       * from edges, which caches results locally.
59       * 
60       * @param g     the graph on which distances will be calculated
61       * @param nev   the class responsible for returning weights for edges
62       */
63      public DijkstraShortestPath(Graph<V,E> g, Function<E, ? extends Number> nev)
64      {
65          super(g, nev);
66      }
67      
68      /**
69       * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
70       * the specified unweighted graph (that is, all weights 1) which
71       * caches results locally.
72       * 
73       * @param g     the graph on which distances will be calculated
74       */ 
75      public DijkstraShortestPath(Graph<V,E> g)
76      {
77          super(g);
78      }
79  
80      /**
81       * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
82       * the specified unweighted graph (that is, all weights 1) which
83       * caches results locally.
84       * 
85       * @param g     the graph on which distances will be calculated
86       * @param cached    specifies whether the results are to be cached
87       */ 
88      public DijkstraShortestPath(Graph<V,E> g, boolean cached)
89      {
90          super(g, cached);
91      }
92      
93      @Override
94      protected SourceData getSourceData(V source)
95      {
96          SourceData sd = sourceMap.get(source);
97          if (sd == null)
98              sd = new SourcePathData(source);
99          return sd;
100     }
101     
102     /**
103      * <p>Returns the last edge on a shortest path from <code>source</code>
104      * to <code>target</code>, or null if <code>target</code> is not 
105      * reachable from <code>source</code>.
106      * 
107      * <p>If either vertex is not in the graph for which this instance
108      * was created, throws <code>IllegalArgumentException</code>.
109      * 
110      * @param source the vertex where the shortest path starts
111      * @param target the vertex where the shortest path ends
112      * @return the last edge on a shortest path from {@code source} to {@code target}
113      *     or null if {@code target} is not reachable from {@code source}
114      */
115 	public E getIncomingEdge(V source, V target)
116 	{
117         if (!g.containsVertex(source))
118             throw new IllegalArgumentException("Specified source vertex " + 
119                     source + " is not part of graph " + g);
120         
121         if (!g.containsVertex(target))
122             throw new IllegalArgumentException("Specified target vertex " + 
123                     target + " is not part of graph " + g);
124 
125         Set<V> targets = new HashSet<V>();
126         targets.add(target);
127         singleSourceShortestPath(source, targets, g.getVertexCount());
128         @SuppressWarnings("unchecked")
129 		Map<V,E> incomingEdgeMap = 
130             ((SourcePathData)sourceMap.get(source)).incomingEdges;
131         E incomingEdge = incomingEdgeMap.get(target);
132         
133         if (!cached)
134             reset(source);
135         
136         return incomingEdge;
137 	}
138 
139     /**
140      * <p>Returns a <code>LinkedHashMap</code> which maps each vertex 
141      * in the graph (including the <code>source</code> vertex) 
142      * to the last edge on the shortest path from the 
143      * <code>source</code> vertex.
144      * The map's iterator will return the elements in order of 
145      * increasing distance from <code>source</code>.
146      * 
147      * @see DijkstraDistance#getDistanceMap(Object,int)
148      * @see DijkstraDistance#getDistance(Object,Object)
149      * @param source    the vertex from which distances are measured
150      */
151     public Map<V,E> getIncomingEdgeMap(V source)
152 	{
153 		return getIncomingEdgeMap(source, g.getVertexCount());
154 	}
155 
156     /**
157      * Returns a <code>List</code> of the edges on the shortest path from 
158      * <code>source</code> to <code>target</code>, in order of their
159      * occurrence on this path.  
160      * If either vertex is not in the graph for which this instance
161      * was created, throws <code>IllegalArgumentException</code>.
162      * 
163      * @param source the starting vertex for the path to generate
164      * @param target the ending vertex for the path to generate
165      * @return the edges on the shortest path from {@code source} to {@code target},
166      *     in order of their occurrence
167      */
168 	public List<E> getPath(V source, V target)
169 	{
170 		if(!g.containsVertex(source)) 
171             throw new IllegalArgumentException("Specified source vertex " + 
172                     source + " is not part of graph " + g);
173         
174 		if(!g.containsVertex(target)) 
175             throw new IllegalArgumentException("Specified target vertex " + 
176                     target + " is not part of graph " + g);
177         
178         LinkedList<E> path = new LinkedList<E>();
179 
180         // collect path data; must use internal method rather than
181         // calling getIncomingEdge() because getIncomingEdge() may
182         // wipe out results if results are not cached
183         Set<V> targets = new HashSet<V>();
184         targets.add(target);
185         singleSourceShortestPath(source, targets, g.getVertexCount());
186         @SuppressWarnings("unchecked")
187 		Map<V,E> incomingEdges = 
188             ((SourcePathData)sourceMap.get(source)).incomingEdges;
189         
190         if (incomingEdges.isEmpty() || incomingEdges.get(target) == null)
191             return path;
192         V current = target;
193         while (!current.equals(source))
194         {
195             E incoming = incomingEdges.get(current);
196             path.addFirst(incoming);
197             current = ((Graph<V,E>)g).getOpposite(current, incoming);
198         }
199 		return path;
200 	}
201 
202     
203     /**
204      * <p>Returns a <code>LinkedHashMap</code> which maps each of the closest 
205      * <code>numDests</code> vertices to the <code>source</code> vertex 
206      * in the graph (including the <code>source</code> vertex) 
207      * to the incoming edge along the path from that vertex.  Throws 
208      * an <code>IllegalArgumentException</code> if <code>source</code>
209      * is not in this instance's graph, or if <code>numDests</code> is 
210      * either less than 1 or greater than the number of vertices in the 
211      * graph.
212      * 
213      * @see #getIncomingEdgeMap(Object)
214      * @see #getPath(Object,Object)
215      * @param source    the vertex from which distances are measured
216      * @param numDests  the number of vertices for which to measure distances
217      * @return a map from each of the closest {@code numDests} vertices
218      *     to the last edge on the shortest path to that vertex starting from {@code source}
219      */
220 	public LinkedHashMap<V,E> getIncomingEdgeMap(V source, int numDests)
221 	{
222         if (g.getVertices().contains(source) == false)
223             throw new IllegalArgumentException("Specified source vertex " + 
224                     source + " is not part of graph " + g);
225 
226         if (numDests < 1 || numDests > g.getVertexCount())
227             throw new IllegalArgumentException("numDests must be >= 1 " + 
228             "and <= g.numVertices()");
229 
230         singleSourceShortestPath(source, null, numDests);
231         
232         @SuppressWarnings("unchecked")
233 		LinkedHashMap<V,E> incomingEdgeMap = 
234             ((SourcePathData)sourceMap.get(source)).incomingEdges;
235         
236         if (!cached)
237             reset(source);
238         
239         return incomingEdgeMap;        
240 	}
241      
242     
243     /**
244      * For a given source vertex, holds the estimated and final distances, 
245      * tentative and final assignments of incoming edges on the shortest path from
246      * the source vertex, and a priority queue (ordered by estimaed distance)
247      * of the vertices for which distances are unknown.
248      * 
249      * @author Joshua O'Madadhain
250      */
251     protected class SourcePathData extends SourceData
252     {
253         protected Map<V,E> tentativeIncomingEdges;
254 		protected LinkedHashMap<V,E> incomingEdges;
255 
256 		protected SourcePathData(V source)
257 		{
258             super(source);
259             incomingEdges = new LinkedHashMap<V,E>();
260             tentativeIncomingEdges = new HashMap<V,E>();
261 		}
262         
263         @Override
264         public void update(V dest, E tentative_edge, double new_dist)
265         {
266             super.update(dest, tentative_edge, new_dist);
267             tentativeIncomingEdges.put(dest, tentative_edge);
268         }
269         
270         @Override
271         public Map.Entry<V,Number> getNextVertex()
272         {
273             Map.Entry<V,Number> p = super.getNextVertex();
274             V v = p.getKey();
275             E incoming = tentativeIncomingEdges.remove(v);
276             incomingEdges.put(v, incoming);
277             return p;
278         }
279         
280         @Override
281         public void restoreVertex(V v, double dist)
282         {
283             super.restoreVertex(v, dist);
284             E incoming = incomingEdges.get(v);
285             tentativeIncomingEdges.put(v, incoming);
286         }
287         
288         @Override
289         public void createRecord(V w, E e, double new_dist)
290         {
291             super.createRecord(w, e, new_dist);
292             tentativeIncomingEdges.put(w, e);
293         }
294        
295     }
296 
297 }