/* * Copyright (c) 2003, the JUNG Project and the Regents of the University * of California * All rights reserved. * * This software is open-source under the BSD license; see either * "license.txt" or * http://jung.sourceforge.net/license.txt for a description. */ package edu.uci.ics.jung.algorithms.shortestpath; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections15.Transformer; import edu.uci.ics.jung.graph.Graph; /** * <p> * Calculates distances and shortest paths using Dijkstra's * single-source-shortest-path algorithm. This is a lightweight extension of * <code>DijkstraDistance</code> that also stores path information, so that the * shortest paths can be reconstructed. * </p> * * <p> * The elements in the maps returned by <code>getIncomingEdgeMap</code> are * ordered (that is, returned by the iterator) by nondecreasing distance from * <code>source</code>. * </p> * * @author Joshua O'Madadhain * @author Tom Nelson converted to jung2 * @see DijkstraDistance */ public class DijkstraShortestPath<V, E> extends DijkstraDistance<V, E> implements ShortestPath<V, E> { /** * <p> * Creates an instance of <code>DijkstraShortestPath</code> for the * specified graph and the specified method of extracting weights from * edges, which caches results locally if and only if <code>cached</code> is * <code>true</code>. * * @param g * the graph on which distances will be calculated * @param nev * the class responsible for returning weights for edges * @param cached * specifies whether the results are to be cached */ public DijkstraShortestPath(Graph<V, E> g, Transformer<E, ? extends Number> nev, boolean cached) { super(g, nev, cached); } /** * <p> * Creates an instance of <code>DijkstraShortestPath</code> for the * specified graph and the specified method of extracting weights from * edges, which caches results locally. * * @param g * the graph on which distances will be calculated * @param nev * the class responsible for returning weights for edges */ public DijkstraShortestPath(Graph<V, E> g, Transformer<E, ? extends Number> nev) { super(g, nev); } /** * <p> * Creates an instance of <code>DijkstraShortestPath</code> for the * specified unweighted graph (that is, all weights 1) which caches results * locally. * * @param g * the graph on which distances will be calculated */ public DijkstraShortestPath(Graph<V, E> g) { super(g); } /** * <p> * Creates an instance of <code>DijkstraShortestPath</code> for the * specified unweighted graph (that is, all weights 1) which caches results * locally. * * @param g * the graph on which distances will be calculated * @param cached * specifies whether the results are to be cached */ public DijkstraShortestPath(Graph<V, E> g, boolean cached) { super(g, cached); } @Override protected SourceData getSourceData(V source) { SourceData sd = sourceMap.get(source); if (sd == null) { sd = new SourcePathData(source); } return sd; } /** * <p> * Returns the last edge on a shortest path from <code>source</code> to * <code>target</code>, or null if <code>target</code> is not reachable from * <code>source</code>. * </p> * * <p> * If either vertex is not in the graph for which this instance was created, * throws <code>IllegalArgumentException</code>. * </p> */ public E getIncomingEdge(V source, V target) { if (!g.containsVertex(source)) { throw new IllegalArgumentException("Specified source vertex " + source + " is not part of graph " + g); } if (!g.containsVertex(target)) { throw new IllegalArgumentException("Specified target vertex " + target + " is not part of graph " + g); } Set<V> targets = new HashSet<V>(); targets.add(target); singleSourceShortestPath(source, targets, g.getVertexCount()); Map<V, E> incomingEdgeMap = ((SourcePathData) sourceMap .get(source)).incomingEdges; E incomingEdge = incomingEdgeMap.get(target); if (!cached) { reset(source); } return incomingEdge; } /** * <p> * Returns a <code>LinkedHashMap</code> which maps each vertex in the graph * (including the <code>source</code> vertex) to the last edge on the * shortest path from the <code>source</code> vertex. The map's iterator * will return the elements in order of increasing distance from * <code>source</code>. * </p> * * @see DijkstraDistance#getDistanceMap(Object,int) * @see DijkstraDistance#getDistance(Object,Object) * @param source * the vertex from which distances are measured */ @Override public Map<V, E> getIncomingEdgeMap(V source) { return getIncomingEdgeMap(source, g.getVertexCount()); } /** * Returns a <code>List</code> of the edges on the shortest path from * <code>source</code> to <code>target</code>, in order of their occurrence * on this path. If either vertex is not in the graph for which this * instance was created, throws <code>IllegalArgumentException</code>. */ public List<E> getPath(V source, V target) { if (!g.containsVertex(source)) { throw new IllegalArgumentException("Specified source vertex " + source + " is not part of graph " + g); } if (!g.containsVertex(target)) { throw new IllegalArgumentException("Specified target vertex " + target + " is not part of graph " + g); } LinkedList<E> path = new LinkedList<E>(); // collect path data; must use internal method rather than // calling getIncomingEdge() because getIncomingEdge() may // wipe out results if results are not cached Set<V> targets = new HashSet<V>(); targets.add(target); singleSourceShortestPath(source, targets, g.getVertexCount()); Map<V, E> incomingEdges = ((SourcePathData) sourceMap .get(source)).incomingEdges; if (incomingEdges.isEmpty() || incomingEdges.get(target) == null) { return path; } V current = target; while (!current.equals(source)) { E incoming = incomingEdges.get(current); path.addFirst(incoming); current = ((Graph<V, E>) g).getOpposite(current, incoming); } return path; } /** * <p> * Returns a <code>LinkedHashMap</code> which maps each of the closest * <code>numDist</code> vertices to the <code>source</code> vertex in the * graph (including the <code>source</code> vertex) to the incoming edge * along the path from that vertex. Throws an * <code>IllegalArgumentException</code> if <code>source</code> is not in * this instance's graph, or if <code>numDests</code> is either less than 1 * or greater than the number of vertices in the graph. * * @see #getIncomingEdgeMap(Object) * @see #getPath(Object,Object) * @param source * the vertex from which distances are measured * @param numDests * the number of vertics for which to measure distances */ public LinkedHashMap<V, E> getIncomingEdgeMap(V source, int numDests) { if (g.getVertices().contains(source) == false) { throw new IllegalArgumentException("Specified source vertex " + source + " is not part of graph " + g); } if (numDests < 1 || numDests > g.getVertexCount()) { throw new IllegalArgumentException( "numDests must be >= 1 " + "and <= g.numVertices()"); } singleSourceShortestPath(source, null, numDests); LinkedHashMap<V, E> incomingEdgeMap = ((SourcePathData) sourceMap .get(source)).incomingEdges; if (!cached) { reset(source); } return incomingEdgeMap; } /** * For a given source vertex, holds the estimated and final distances, * tentative and final assignments of incoming edges on the shortest path * from the source vertex, and a priority queue (ordered by estimaed * distance) of the vertices for which distances are unknown. * * @author Joshua O'Madadhain */ protected class SourcePathData extends SourceData { protected Map<V, E> tentativeIncomingEdges; protected LinkedHashMap<V, E> incomingEdges; protected SourcePathData(V source) { super(source); incomingEdges = new LinkedHashMap<V, E>(); tentativeIncomingEdges = new HashMap<V, E>(); } @Override public void update(V dest, E tentative_edge, double new_dist) { super.update(dest, tentative_edge, new_dist); tentativeIncomingEdges.put(dest, tentative_edge); } @Override public Map.Entry<V, Number> getNextVertex() { Map.Entry<V, Number> p = super.getNextVertex(); V v = p.getKey(); E incoming = tentativeIncomingEdges.remove(v); incomingEdges.put(v, incoming); return p; } @Override public void restoreVertex(V v, double dist) { super.restoreVertex(v, dist); E incoming = incomingEdges.get(v); tentativeIncomingEdges.put(v, incoming); } @Override public void createRecord(V w, E e, double new_dist) { super.createRecord(w, e, new_dist); tentativeIncomingEdges.put(w, e); } } }