/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id: DijkstraAlgorithm.java 1681108 2015-05-22 13:26:12Z ssteiner $ */
package org.apache.xmlgraphics.util.dijkstra;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
This is an implementation of Dijkstra's algorithm to find the shortest path for a directed
graph with non-negative edge weights.
See Also:
/**
* This is an implementation of Dijkstra's algorithm to find the shortest path for a directed
* graph with non-negative edge weights.
* @see <a href="http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm">WikiPedia on Dijkstra's
* Algorithm</a>
*/
public class DijkstraAlgorithm {
Infinity value for distances. /** Infinity value for distances. */
public static final int INFINITE = Integer.MAX_VALUE;
Compares penalties between two possible destinations. /** Compares penalties between two possible destinations. */
private final Comparator penaltyComparator = new Comparator() {
public int compare(Object left, Object right) {
int leftPenalty = getLowestPenalty((Vertex)left);
int rightPenalty = getLowestPenalty((Vertex)right);
if (leftPenalty < rightPenalty) {
return -1;
} else if (leftPenalty == rightPenalty) {
return ((Comparable)left).compareTo(right);
} else {
return 1;
}
}
};
The directory of edges /** The directory of edges */
private EdgeDirectory edgeDirectory;
The priority queue for all vertices under inspection, ordered by penalties/distances. /** The priority queue for all vertices under inspection, ordered by penalties/distances. */
private TreeSet priorityQueue = new TreeSet(penaltyComparator);
//Set<Vertex>
The set of vertices for which the lowest penalty has been found. /** The set of vertices for which the lowest penalty has been found. */
private Set finishedVertices = new java.util.HashSet();
//Set<Vertex>
The currently known lowest penalties for all vertices. /** The currently known lowest penalties for all vertices. */
private Map lowestPenalties = new java.util.HashMap();
//Map<Vertex,Integer>
Map of all predecessors in the spanning tree of best routes. /** Map of all predecessors in the spanning tree of best routes. */
private Map predecessors = new java.util.HashMap();
//Map<Vertex,Vertex>
Main Constructor.
Params: - edgeDirectory – the edge directory this instance should work on
/**
* Main Constructor.
* @param edgeDirectory the edge directory this instance should work on
*/
public DijkstraAlgorithm(EdgeDirectory edgeDirectory) {
this.edgeDirectory = edgeDirectory;
}
Returns the penalty between two vertices.
Params: - start – the start vertex
- end – the end vertex
Returns: the penalty between two vertices, or 0 if no single edge between the two vertices
exists.
/**
* Returns the penalty between two vertices.
* @param start the start vertex
* @param end the end vertex
* @return the penalty between two vertices, or 0 if no single edge between the two vertices
* exists.
*/
protected int getPenalty(Vertex start, Vertex end) {
return this.edgeDirectory.getPenalty(start, end);
}
Returns an iterator over all valid destinations for a given vertex.
Params: - origin – the origin from which to search for destinations
Returns: the iterator over all valid destinations for a given vertex
/**
* Returns an iterator over all valid destinations for a given vertex.
* @param origin the origin from which to search for destinations
* @return the iterator over all valid destinations for a given vertex
*/
protected Iterator getDestinations(Vertex origin) {
return this.edgeDirectory.getDestinations(origin);
}
private void reset() {
finishedVertices.clear();
priorityQueue.clear();
lowestPenalties.clear();
predecessors.clear();
}
Run Dijkstra's shortest path algorithm. After this method is finished you can use getPredecessor(Vertex)
to reconstruct the best/shortest path starting from the destination backwards. Params: - start – the starting vertex
- destination – the destination vertex.
/**
* Run Dijkstra's shortest path algorithm. After this method is finished you can use
* {@link #getPredecessor(Vertex)} to reconstruct the best/shortest path starting from the
* destination backwards.
* @param start the starting vertex
* @param destination the destination vertex.
*/
public void execute(Vertex start, Vertex destination) {
if (start == null || destination == null) {
throw new NullPointerException("start and destination may not be null");
}
reset();
setShortestDistance(start, 0);
priorityQueue.add(start);
// the current node
Vertex u;
// extract the vertex with the shortest distance
while (priorityQueue.size() > 0) {
u = (Vertex)priorityQueue.first();
priorityQueue.remove(u);
if (destination.equals(u)) {
//Destination reached
break;
}
finishedVertices.add(u);
relax(u);
}
}
Compute new lowest penalties for neighboring vertices. Update the lowest penalties and the
predecessor map if a better solution is found.
Params: - u – the vertex to process
/**
* Compute new lowest penalties for neighboring vertices. Update the lowest penalties and the
* predecessor map if a better solution is found.
* @param u the vertex to process
*/
private void relax(Vertex u) {
Iterator iter = getDestinations(u);
while (iter.hasNext()) {
Vertex v = (Vertex)iter.next();
// skip node already settled
if (isFinished(v)) {
continue;
}
int shortDist = getLowestPenalty(u) + getPenalty(u, v);
if (shortDist < getLowestPenalty(v)) {
// assign new shortest distance and mark unsettled
setShortestDistance(v, shortDist);
// assign predecessor in shortest path
setPredecessor(v, u);
}
}
}
private void setPredecessor(Vertex a, Vertex b) {
predecessors.put(a, b);
}
Indicates whether a shortest route to a vertex has been found.
Params: - v – the vertex
Returns: true if the shortest route to this vertex has been found.
/**
* Indicates whether a shortest route to a vertex has been found.
* @param v the vertex
* @return true if the shortest route to this vertex has been found.
*/
private boolean isFinished(Vertex v) {
return finishedVertices.contains(v);
}
private void setShortestDistance(Vertex vertex, int distance) {
//Remove so it is inserted at the right position after the lowest penalty changes for this
//vertex.
priorityQueue.remove(vertex);
//Update the lowest penalty.
lowestPenalties.put(vertex, distance);
//Insert the vertex again at the new position based on the lowest penalty
priorityQueue.add(vertex);
}
Returns the lowest penalty from the start point to a given vertex.
Params: - vertex – the vertex
Returns: the lowest penalty or INFINITE
if there is no route to the destination.
/**
* Returns the lowest penalty from the start point to a given vertex.
* @param vertex the vertex
* @return the lowest penalty or {@link DijkstraAlgorithm#INFINITE} if there is no route to
* the destination.
*/
public int getLowestPenalty(Vertex vertex) {
Integer d = ((Integer)lowestPenalties.get(vertex));
return (d == null) ? INFINITE : d;
}
Returns the vertex's predecessor on the shortest path.
Params: - vertex – the vertex for which to find the predecessor
Returns: the vertex's predecessor on the shortest path, or
null
if there is no route to the destination.
/**
* Returns the vertex's predecessor on the shortest path.
* @param vertex the vertex for which to find the predecessor
* @return the vertex's predecessor on the shortest path, or
* <code>null</code> if there is no route to the destination.
*/
public Vertex getPredecessor(Vertex vertex) {
return (Vertex)predecessors.get(vertex);
}
}