blob: c658b9a313f269176296433030dea47785fd4dd1 [file] [log] [blame]
use std::collections::{
HashMap,
BinaryHeap,
};
use std::collections::hash_map::Entry::{
Occupied,
Vacant,
};
use std::default::Default;
use std::hash::Hash;
use std::ops::{
Add,
};
use super::MinScored;
use super::visit::{
Visitable,
VisitMap,
};
/// Dijkstra's shortest path algorithm.
pub fn dijkstra<'a, G: Visitable, K, F, Edges>(graph: &'a G,
start: G::NodeId,
goal: Option<G::NodeId>,
mut edges: F) -> HashMap<G::NodeId, K> where
G::NodeId: Clone + Eq + Hash,
K: Default + Add<Output=K> + Copy + PartialOrd,
F: FnMut(&'a G, G::NodeId) -> Edges,
Edges: Iterator<Item=(G::NodeId, K)>,
<G as Visitable>::Map: VisitMap<G::NodeId>,
{
let mut visited = graph.visit_map();
let mut scores = HashMap::new();
let mut predecessor = HashMap::new();
let mut visit_next = BinaryHeap::new();
let zero_score: K = Default::default();
scores.insert(start.clone(), zero_score);
visit_next.push(MinScored(zero_score, start));
while let Some(MinScored(node_score, node)) = visit_next.pop() {
if visited.is_visited(&node) {
continue
}
if goal.as_ref() == Some(&node) {
break
}
for (next, edge) in edges(graph, node.clone()) {
if visited.is_visited(&next) {
continue
}
let mut next_score = node_score + edge;
match scores.entry(next.clone()) {
Occupied(ent) => if next_score < *ent.get() {
*ent.into_mut() = next_score;
predecessor.insert(next.clone(), node.clone());
} else {
next_score = *ent.get();
},
Vacant(ent) => {
ent.insert(next_score);
predecessor.insert(next.clone(), node.clone());
}
}
visit_next.push(MinScored(next_score, next));
}
visited.visit(node);
}
scores
}