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 }