private void dijkstra(Node origin) {
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;
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));
}
}
}
}
}
|
The code follows very closely the statement of the algorithm above.
The principal point of difference concerns initialisation, where we
have adopted a simplification. Remember that
corresponds to origin,
corresponds to settled and
corresponds
to visited. The initialisation in the first eight lines
corresponds to
and
with
.
However we shall get exactly the initialisation given in the proof
above after the first time round the main while loop.
The main while loop first removes the item from the top of the
heap. This corresponds to D1. If v is not
already settled, then settle it and consider all nodes w
adjacent to it by using the tips iterator. If w has
not yet been visited, or if there is now a shorter path to w of
length d, then insert the pair
into the
heap. The algorithm terminates when the heap is empty.
This implementation uses the simple
PriorityQueue
that was implemented before. Ideally the heap should represent the
function
and a function only assigns a single value to its
argument. For any node w, the heap should contain at most one
entry of the form
; it ought not to contain both
and
. This means that in the
last line of code, if
is already in the heap, and
, we ought to replace
by
, and then reorder the heap if necessary. This is
not difficult in principle, though it requires lengthier code and
book-keeping. The code in Figure 13.3 simply leaves the old entry
in the heap and ignores it when it emerges. This is the reason for
the check
if (!vv.settled) {..}
at the beginning of the main while loop. Once v has
emerged from the heap, any other entry for v in the heap is out
of date and can be ignored. This also provides a simple way of
dealing with multiple arcs between nodes. They all get thrown into
the heap (if they are visited in decreasing order) and the heap
decides which is the smallest.