A (fairly) general purpose implementation of Dijkstra's shortest path
algorithm. git-svn-id: https://samskivert.googlecode.com/svn/trunk@1537 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2004 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
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.
|
||||
*/
|
||||
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
|
||||
{
|
||||
/** Enumerates all nodes in the graph. */
|
||||
public Iterator enumerateNodes ();
|
||||
|
||||
/** Returns the list of the edges for the specified node. */
|
||||
public List getEdges (Object 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 node opposite the supplied node on the supplied edge. */
|
||||
public Object getOpposite (Object edge, Object 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.
|
||||
*
|
||||
* @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 compute (Graph graph, Object start, Object end)
|
||||
{
|
||||
HashMap nodes = new HashMap();
|
||||
HashSet relaxed = new HashSet();
|
||||
SortableArrayList uptight = new SortableArrayList();
|
||||
|
||||
// initialize our searching info
|
||||
for (Iterator iter = graph.enumerateNodes(); iter.hasNext(); ) {
|
||||
NodeInfo info = new NodeInfo();
|
||||
info.node = iter.next();
|
||||
if (info.node == start) {
|
||||
info.weightTo = 0;
|
||||
}
|
||||
uptight.add(info);
|
||||
nodes.put(info.node, info);
|
||||
}
|
||||
uptight.sort();
|
||||
|
||||
// now execute the main part of the search
|
||||
while (uptight.size() > 0) {
|
||||
// remove the cheapest known node
|
||||
NodeInfo info = (NodeInfo)uptight.remove(uptight.size()-1);
|
||||
// make a note that it is now relaxed
|
||||
relaxed.add(info.node);
|
||||
// relax its uptight neighbors
|
||||
List 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);
|
||||
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 = (NodeInfo)nodes.get(onode);
|
||||
int weight = graph.computeWeight(edge, info.node);
|
||||
if (oinfo.weightTo > info.weightTo + weight) {
|
||||
oinfo.weightTo = info.weightTo + weight;
|
||||
oinfo.edgeTo = edge;
|
||||
}
|
||||
}
|
||||
// now resort the uptight list
|
||||
uptight.sort();
|
||||
}
|
||||
|
||||
// now trace the path from the final node back to the start
|
||||
ArrayList path = new ArrayList();
|
||||
NodeInfo info = (NodeInfo)nodes.get(end);
|
||||
while (info.edgeTo != null) {
|
||||
path.add(0, info.edgeTo);
|
||||
info = (NodeInfo)nodes.get(
|
||||
graph.getOpposite(info.edgeTo, info.node));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/** Used to maintain information during the shortest path search. */
|
||||
protected static final class NodeInfo
|
||||
implements Comparable
|
||||
{
|
||||
/** The node for which we're representing information. */
|
||||
public Object 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;
|
||||
|
||||
/** We order ourselves by the cumulative weight to this node. */
|
||||
public int compareTo (Object o)
|
||||
{
|
||||
return ((NodeInfo)o).weightTo - weightTo;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user