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; import java.util.List;
/** /**
* Implements Dijkstra's algorithm for finding the shortest path between * Implements Dijkstra's algorithm for finding the shortest path between two nodes in a weighted
* two nodes in a weighted graph. The code assumes that the caller * graph. The code assumes that the caller represents their nodes and edges as objects which can
* represents their nodes and edges as objects which can be compared and * be compared and hashed (via {@link Object#equals} and {@link Object#hashCode}) other necessary
* hashed (via {@link Object#equals} and {@link Object#hashCode}) other * information is obtained through a special interface {@link Graph} implemented by the caller to
* necessary information is obtained through a special interface {@link * enumerate edges and compute weights.
* Graph} implemented by the caller to enumerate edges and compute
* weights.
*/ */
public class ShortestPath public class ShortestPath
{ {
/** A caller must implement this interface to provide the information /** A caller must implement this interface to provide the information needed to define the
* needed to define the graph and compute the shortest path. */ * graph and compute the shortest path.
public interface Graph */
public interface Graph<T, V>
{ {
/** Enumerates all nodes in the graph. */ /** Enumerates all nodes in the graph. */
public Iterator<Object> enumerateNodes (); public Iterator<T> enumerateNodes ();
/** Returns the list of the edges for the specified node. */ /** 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 /** Returns the weight associated with the supplied edge in the direction established by
* direction established by the supplied starting node. */ * the supplied starting node. */
public int computeWeight (Object edge, Object start); public int computeWeight (V edge, T start);
/** Returns the node opposite the supplied node on the supplied edge. */ /** 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 * Computes the shortest path between the specified starting and ending nodes using Dijkstra's
* ending nodes using Dijkstra's algorithm. This implementation * algorithm. This implementation assumes that the graph is properly formed and may behave
* assumes that the graph is properly formed and may behave strangely * strangely or throw an exception if provided with an invalid graph.
* or throw an exception if provided with an invalid graph.
* *
* @return a list of the edges that must be followed to traverse from the * @return a list of the edges that must be followed to traverse from the starting node to the
* starting node to the ending node. This list may be empty if the graph is * ending node. This list may be empty if the graph is improperly formed.
* 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>(); HashMap<T, NodeInfo<T, V>> nodes = new HashMap<T, NodeInfo<T, V>>();
HashSet<Object> relaxed = new HashSet<Object>(); HashSet<T> relaxed = new HashSet<T>();
ComparableArrayList<NodeInfo> uptight = ComparableArrayList<NodeInfo<T, V>> uptight = new ComparableArrayList<NodeInfo<T, V>>();
new ComparableArrayList<NodeInfo>();
// initialize our searching info // initialize our searching info
for (Iterator<Object> iter = graph.enumerateNodes(); iter.hasNext(); ) { for (Iterator<T> iter = graph.enumerateNodes(); iter.hasNext(); ) {
NodeInfo info = new NodeInfo(); NodeInfo<T, V> info = new NodeInfo<T, V>();
info.node = iter.next(); info.node = iter.next();
if (info.node == start) { if (info.node == start) {
info.weightTo = 0; info.weightTo = 0;
@@ -87,21 +83,20 @@ public class ShortestPath
// now execute the main part of the search // now execute the main part of the search
while (uptight.size() > 0) { while (uptight.size() > 0) {
// remove the cheapest known node // 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 // make a note that it is now relaxed
relaxed.add(info.node); relaxed.add(info.node);
// relax its uptight neighbors // 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++) { for (int ii = 0, ll = edges.size(); ii < ll; ii++) {
Object edge = edges.get(ii); V edge = edges.get(ii);
Object onode = graph.getOpposite(edge, info.node); T onode = graph.getOpposite(edge, info.node);
if (relaxed.contains(onode)) { if (relaxed.contains(onode)) {
continue; continue;
} }
// if the path through this node to its neighbor is // if the path through this node to its neighbor is cheaper than the existing known
// cheaper than the existing known shortest path, update // shortest path, update the neighbor to reflect this new shorter path
// the neighbor to reflect this new shorter path NodeInfo<T, V> oinfo = nodes.get(onode);
NodeInfo oinfo = nodes.get(onode);
int weight = graph.computeWeight(edge, info.node); int weight = graph.computeWeight(edge, info.node);
if (oinfo.weightTo > info.weightTo + weight) { if (oinfo.weightTo > info.weightTo + weight) {
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 // now trace the path from the final node back to the start
ArrayList<Object> path = new ArrayList<Object>(); ArrayList<V> path = new ArrayList<V>();
NodeInfo info = nodes.get(end); NodeInfo<T, V> info = nodes.get(end);
while (info.edgeTo != null) { while (info.edgeTo != null) {
path.add(0, info.edgeTo); path.add(0, info.edgeTo);
info = nodes.get(graph.getOpposite(info.edgeTo, info.node)); 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. */ /** 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. */ /** The node for which we're representing information. */
public Object node; public T node;
/** The cumulative weight to this node from the source. */ /** The cumulative weight to this node from the source. */
public int weightTo = Integer.MAX_VALUE / 4; public int weightTo = Integer.MAX_VALUE / 4;
/** The edge followed to reach this node along the shortest path. */ /** 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. */ /** 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; return o.weightTo - weightTo;
} }
} }