package DataStructures; import java.util.Enumeration; import java.util.Iterator; import java.util.NoSuchElementException; /** *
A class implementing shortest path algorithms for positively * weighted directed graphs.
* * @author Peter Williams */ public class PositiveWeightedGraph extends WeightedGraph { private Node source; // source when Dijkstra was last called private int version; // for detecting changes to parent graph /** * Constructs an empty positive weighted graph */ public PositiveWeightedGraph() { source = null; version = super.version - 1; } /** * Tests whether a path exists between two given nodes. * @paramorigin the node at which to begin,
* @param destination the node at which to end.
* @return true if a path exists, false otherwise.
* @exception NoSuchElementException if either node does not exist.
* @exception ArithmeticException if a negatively weighted arc is
* encountered in the search.
*/
public boolean pathExists(Object origin, Object destination) {
Node source = (Node) nodeMap.lookup(origin);
Node target = (Node) nodeMap.lookup(destination);
dijkstra(source);
return ((WorkSpace) target.work).settled;
}
/**
* Returns the length of a shortest path between two given nodes.
* @param origin the node at which to begin,
* @param destination the node at which to end.
* @return the shortest path length.
* @exception NoSuchElementException if either node does not exist,
* or if there is no path between them.
* @exception ArithmeticException if a negatively weighted arc is
* encountered in the search.
*/
public int shortestPathLength(Object origin, Object destination) {
Node source = (Node) nodeMap.lookup(origin);
Node target = (Node) nodeMap.lookup(destination);
dijkstra(source);
if (((WorkSpace) target.work).settled) {
return ((WorkSpace) target.work).distance;
} else {
throw new NoSuchElementException();
}
}
/**
* Enumerates the nodes in a shortest path between two given nodes.
* @param origin the node at which to begin,
* @param destination the node at which to end.
* @return the enumeration.
* @exception NoSuchElementException if either node does not exist,
* or if there is no path between them.
* @exception ArithmeticException if a negatively weighted arc is
* encountered in the search.
*/
public Enumeration shortestPath(Object origin, Object destination) {
final Node source = (Node) nodeMap.lookup(origin);
final Node target = (Node) nodeMap.lookup(destination);
dijkstra(source);
if (!((WorkSpace) target.work).settled) {
throw new NoSuchElementException();
}
return new Enumeration() {
// use a stack to reverse the order
private Stack s = new StackArray();
private Node current = target;
{ // load the stack
while (current != source) {
s.push(current);
current = ((WorkSpace) current.work).previous;
}
s.push(source);
}
public boolean hasMoreElements() {
return !s.isEmpty();
}
public Object nextElement() {
if (!s.isEmpty()) {
return ((Node) s.pop()).label;
} else {
throw new NoSuchElementException();
}
}
};
}
// workspace for each node for single-source shortest path algorithm
private class WorkSpace {
boolean settled; // whether the shortest route to this node is known
boolean visited; // true if a settled node or adjacent to a settled node
int distance; // shortest known distance to this node from source
Node previous; // previous node on shortest path
}
// items for the binary heap used in Dijkstra's algorithm
private class HeapItem implements Comparable {
Node node;
int distance;
HeapItem(Node node, int distance) {
this.node = node;
this.distance = distance;
}
// highest priority given to lowest weight
public int compareTo(Object other) {
return ((HeapItem) other).distance - distance;
}
}
// the basic algorithm
private void dijkstra(Node origin) {
// don't run if nothing has changed
if (version == super.version && origin == source) {
return;
}
// allocate new work space if necessary
if (version != super.version) {
Iterator i = nodeMap.iterator();
while (i.hasNext()) {
Node n = (Node) ((LookupTable.Entry) i.next()).value();
if (n.work == null) {
n.work = new WorkSpace();
}
}
}
// solve the single-source all-paths problem
// initialise
Iterator i = nodeMap.iterator();
while (i.hasNext()) {
Node n = (Node) ((LookupTable.Entry) i.next()).value();
((WorkSpace) n.work).visited = false;
((WorkSpace) n.work).settled = false;
}
((WorkSpace) origin.work).visited = true;
((WorkSpace) origin.work).distance = 0;
// find the shortest paths
PriorityQueue q = new BinaryHeap();
q.add(new HeapItem(origin, 0));
while (!q.isEmpty()) {
Node v = ((HeapItem) q.remove()).node;
WorkSpace vv = (WorkSpace) v.work;
if (!vv.settled) {
vv.settled = true;
Iterator tips = v.adjacent.iterator();
while (tips.hasNext()) {
Tip t = (Tip) tips.next();
Node w = t.head;
int c = t.weight;
if (c < 0) {
throw new ArithmeticException("Negative weight in graph");
}
int d = vv.distance + c;
WorkSpace ww = (WorkSpace) w.work;
if (!ww.visited || ww.distance > d) {
ww.visited = true;
ww.distance = d;
ww.previous = v;
q.add(new HeapItem(w, d));
}
}
}
}
version = super.version;
source = origin;
}
}