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