152 строки
5,8 КиБ
Java
152 строки
5,8 КиБ
Java
package org.insa.graphs.algorithm.shortestpath;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
|
|
import org.insa.graphs.algorithm.AbstractSolution.Status;
|
|
import org.insa.graphs.algorithm.utils.BinaryHeap;
|
|
import org.insa.graphs.model.Arc;
|
|
import org.insa.graphs.model.Graph;
|
|
import org.insa.graphs.model.Node;
|
|
import org.insa.graphs.model.Path;
|
|
|
|
public class DijkstraAlgorithm extends ShortestPathAlgorithm {
|
|
|
|
public DijkstraAlgorithm(ShortestPathData data) {
|
|
super(data);
|
|
}
|
|
|
|
protected Label createLabel(Node node) {
|
|
return new Label(node);
|
|
}
|
|
|
|
@Override
|
|
protected ShortestPathSolution doRun() {
|
|
|
|
final ShortestPathData data = getInputData();
|
|
ShortestPathSolution solution = null;
|
|
|
|
final Graph graph = data.getGraph();
|
|
|
|
final int nbNodes = graph.size();
|
|
|
|
// List of sommets, but with type Label
|
|
ArrayList<Label> labels = new ArrayList<Label>(nbNodes);
|
|
|
|
// Heap of sommets
|
|
BinaryHeap<Label> tas = new BinaryHeap<Label>();
|
|
|
|
// Notify observers about the first event (origin processed).
|
|
notifyOriginProcessed(data.getOrigin());
|
|
|
|
// Init Dijkstra
|
|
for (Node node : graph.getNodes()) {
|
|
// Luckily they are ordered by id.
|
|
// ArrayList.set only works if the value is already initialized (because why Java?)
|
|
labels.add(this.createLabel(node));
|
|
}
|
|
|
|
Label s = labels.get(data.getOrigin().getId());
|
|
|
|
// Add origin in the heap
|
|
// Label s = origin;//labels.get(0);
|
|
s.setPathCost(0);
|
|
tas.insert(s);
|
|
|
|
// On considère le sommet x à chaque itération
|
|
Label x = s;
|
|
|
|
final int dest_id = data.getDestination().getId();
|
|
while (!tas.isEmpty() && !(labels.get(dest_id).getNode().equals(x.getNode()))) {
|
|
x = tas.deleteMin();
|
|
x.mark();
|
|
notifyNodeMarked(x.getNode());
|
|
// System.out.println(x.getCost()); // Pour vérifier une croissance des noeuds marqués
|
|
// We create a list of node successors of x, instead of a list of Arcs.
|
|
double arc_cost = 0;
|
|
for (Arc successorArc : x.getNode().getSuccessors()) {
|
|
Label successor = labels.get(successorArc.getDestination().getId());
|
|
|
|
if (!successor.isMarked() && data.isAllowed(successorArc)) {
|
|
// This loop serves to get the length of the arc as
|
|
// we know its origin and destination
|
|
for (Arc arc : x.getNode().getSuccessors()) {
|
|
|
|
if (successor.getNode().equals(arc.getDestination())) {
|
|
// data.getcost(arc) returns a cost considering the mode chosen:
|
|
// TIME or LENGTH
|
|
// Similar to using getLength / getMinimumTravelTime
|
|
arc_cost = data.getCost(arc);
|
|
}
|
|
}
|
|
|
|
final double possible_path_cost = x.getCost() + arc_cost;
|
|
if (successor.getCost() > possible_path_cost) {
|
|
// Mise à jour du label
|
|
successor.setPathCost(possible_path_cost);
|
|
successor.setParentNode(x.getNode());
|
|
// Si le noeud n'a pas déjà été rajouté au tas, on le rajoute
|
|
// isReached permet de vérifier en complexité O(1)
|
|
// C'est un léger coût en mémoire pour un gain en vitesse
|
|
if (successor.isReached()) {
|
|
// removing then inserting resorts the binary heap
|
|
tas.remove(successor);
|
|
} else {
|
|
successor.markReached();
|
|
notifyNodeReached(successor.getNode());
|
|
}
|
|
tas.insert(successor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (labels.get(data.getDestination().getId()).getParentNode() == null) {
|
|
this.pathCost = 0;
|
|
solution = new ShortestPathSolution(data, Status.INFEASIBLE);
|
|
}
|
|
else {
|
|
// Create the path ...
|
|
ArrayList<Arc> arcs_path = new ArrayList<>();
|
|
|
|
// We will find the path using the parent nodes, from the destination to the
|
|
// origin
|
|
Label current_label = labels.get(data.getDestination().getId());
|
|
Label parent_label = current_label;
|
|
|
|
while(current_label != null && current_label.getNode().getId() != data.getOrigin().getId())
|
|
{
|
|
// Find the label matching the parent node
|
|
parent_label = labels.get(current_label.getParentNode().getId());
|
|
|
|
// Knowing the parent node, get the arc between the parent and
|
|
// current node and add it to the path
|
|
if (parent_label != null) {
|
|
double minCost = Double.MAX_VALUE;
|
|
Arc minCostArc = null;
|
|
for (Arc arc : parent_label.getNode().getSuccessors()) {
|
|
if (arc.getDestination().getId() == current_label.getNode().getId()
|
|
&& data.isAllowed(arc)
|
|
&& data.getCost(arc) < minCost) {
|
|
minCost = data.getCost(arc);
|
|
minCostArc = arc;
|
|
}
|
|
}
|
|
arcs_path.add(minCostArc);
|
|
}
|
|
current_label = parent_label;
|
|
}
|
|
|
|
notifyDestinationReached(data.getDestination());
|
|
|
|
// Reverse the path...
|
|
Collections.reverse(arcs_path);
|
|
|
|
// Create the final solution.
|
|
solution = new ShortestPathSolution(data, Status.OPTIMAL, new Path(graph, arcs_path));
|
|
|
|
this.pathCost = labels.get(data.getDestination().getId()).getCost();
|
|
}
|
|
return solution;
|
|
}
|
|
}
|