Patch from Dave to genericize ShortestPath.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2574 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
samskivert
2009-06-09 18:36:20 +00:00
parent f3710b7367
commit 75a28c890c
+39 -45
View File
@@ -27,54 +27,50 @@ import java.util.Iterator;
import java.util.List;
/**
* Implements Dijkstra's algorithm for finding the shortest path between
* two nodes in a weighted graph. The code assumes that the caller
* represents their nodes and edges as objects which can be compared and
* hashed (via {@link Object#equals} and {@link Object#hashCode}) other
* necessary information is obtained through a special interface {@link
* Graph} implemented by the caller to enumerate edges and compute
* weights.
* Implements Dijkstra's algorithm for finding the shortest path between two nodes in a weighted
* graph. The code assumes that the caller represents their nodes and edges as objects which can
* be compared and hashed (via {@link Object#equals} and {@link Object#hashCode}) other necessary
* information is obtained through a special interface {@link Graph} implemented by the caller to
* enumerate edges and compute weights.
*/
public class ShortestPath
{
/** A caller must implement this interface to provide the information
* needed to define the graph and compute the shortest path. */
public interface Graph
/** A caller must implement this interface to provide the information needed to define the
* graph and compute the shortest path.
*/
public interface Graph<T, V>
{
/** Enumerates all nodes in the graph. */
public Iterator<Object> enumerateNodes ();
public Iterator<T> enumerateNodes ();
/** Returns the list of the edges for the specified node. */
public List<Object> getEdges (Object node);
public List<V> getEdges (T node);
/** Returns the weight associated with the supplied edge in the
* direction established by the supplied starting node. */
public int computeWeight (Object edge, Object start);
/** Returns the weight associated with the supplied edge in the direction established by
* the supplied starting node. */
public int computeWeight (V edge, T start);
/** Returns the node opposite the supplied node on the supplied edge. */
public Object getOpposite (Object edge, Object node);
public T getOpposite (V edge, T node);
}
/**
* Computes the shortest path between the specified starting and
* ending nodes using Dijkstra's algorithm. This implementation
* assumes that the graph is properly formed and may behave strangely
* or throw an exception if provided with an invalid graph.
* Computes the shortest path between the specified starting and ending nodes using Dijkstra's
* algorithm. This implementation assumes that the graph is properly formed and may behave
* strangely or throw an exception if provided with an invalid graph.
*
* @return a list of the edges that must be followed to traverse from the
* starting node to the ending node. This list may be empty if the graph is
* improperly formed.
* @return a list of the edges that must be followed to traverse from the starting node to the
* ending node. This list may be empty if the graph is improperly formed.
*/
public static List<Object> compute (Graph graph, Object start, Object end)
public static <T, V> List<V> compute (Graph<T, V> graph, T start, T end)
{
HashMap<Object,NodeInfo> nodes = new HashMap<Object,NodeInfo>();
HashSet<Object> relaxed = new HashSet<Object>();
ComparableArrayList<NodeInfo> uptight =
new ComparableArrayList<NodeInfo>();
HashMap<T, NodeInfo<T, V>> nodes = new HashMap<T, NodeInfo<T, V>>();
HashSet<T> relaxed = new HashSet<T>();
ComparableArrayList<NodeInfo<T, V>> uptight = new ComparableArrayList<NodeInfo<T, V>>();
// initialize our searching info
for (Iterator<Object> iter = graph.enumerateNodes(); iter.hasNext(); ) {
NodeInfo info = new NodeInfo();
for (Iterator<T> iter = graph.enumerateNodes(); iter.hasNext(); ) {
NodeInfo<T, V> info = new NodeInfo<T, V>();
info.node = iter.next();
if (info.node == start) {
info.weightTo = 0;
@@ -87,21 +83,20 @@ public class ShortestPath
// now execute the main part of the search
while (uptight.size() > 0) {
// remove the cheapest known node
NodeInfo info = uptight.remove(uptight.size()-1);
NodeInfo<T, V> info = uptight.remove(uptight.size()-1);
// make a note that it is now relaxed
relaxed.add(info.node);
// relax its uptight neighbors
List<Object> edges = graph.getEdges(info.node);
List<V> edges = graph.getEdges(info.node);
for (int ii = 0, ll = edges.size(); ii < ll; ii++) {
Object edge = edges.get(ii);
Object onode = graph.getOpposite(edge, info.node);
V edge = edges.get(ii);
T onode = graph.getOpposite(edge, info.node);
if (relaxed.contains(onode)) {
continue;
}
// if the path through this node to its neighbor is
// cheaper than the existing known shortest path, update
// the neighbor to reflect this new shorter path
NodeInfo oinfo = nodes.get(onode);
// if the path through this node to its neighbor is cheaper than the existing known
// shortest path, update the neighbor to reflect this new shorter path
NodeInfo<T, V> oinfo = nodes.get(onode);
int weight = graph.computeWeight(edge, info.node);
if (oinfo.weightTo > info.weightTo + weight) {
oinfo.weightTo = info.weightTo + weight;
@@ -113,8 +108,8 @@ public class ShortestPath
}
// now trace the path from the final node back to the start
ArrayList<Object> path = new ArrayList<Object>();
NodeInfo info = nodes.get(end);
ArrayList<V> path = new ArrayList<V>();
NodeInfo<T, V> info = nodes.get(end);
while (info.edgeTo != null) {
path.add(0, info.edgeTo);
info = nodes.get(graph.getOpposite(info.edgeTo, info.node));
@@ -123,20 +118,19 @@ public class ShortestPath
}
/** Used to maintain information during the shortest path search. */
protected static final class NodeInfo implements Comparable<NodeInfo>
protected static final class NodeInfo<T, V> implements Comparable<NodeInfo<T, V>>
{
/** The node for which we're representing information. */
public Object node;
public T node;
/** The cumulative weight to this node from the source. */
public int weightTo = Integer.MAX_VALUE / 4;
/** The edge followed to reach this node along the shortest path. */
public Object edgeTo;
public V edgeTo;
/** We order ourselves by the cumulative weight to this node. */
public int compareTo (NodeInfo o)
{
public int compareTo (NodeInfo<T, V> o) {
return o.weightTo - weightTo;
}
}