Use PriorityQueue for Dijkstra's, requires Hash for Vertex
This commit is contained in:
+25
-34
@@ -28,30 +28,14 @@
|
||||
//! [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
||||
//! [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BinaryHeap, VecDeque};
|
||||
use priority_queue::PriorityQueue;
|
||||
use std::cmp::Reverse;
|
||||
use std::collections::VecDeque;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::maps::EntityMap;
|
||||
use crate::traits::{GraphTopology, IncidenceCursor};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct DistanceOrderedVertex<V> {
|
||||
distance: u32,
|
||||
vertex: V,
|
||||
}
|
||||
|
||||
impl<V: Eq> PartialOrd for DistanceOrderedVertex<V> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Eq> Ord for DistanceOrderedVertex<V> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
other.distance.cmp(&self.distance)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return data type for [`dijkstra`] and [`dijkstra_unweighted`].
|
||||
pub struct DijkstraResult<V: Copy> {
|
||||
/// Vertex map of minimum distances from a given `source` vertex.
|
||||
@@ -61,7 +45,7 @@ pub struct DijkstraResult<V: Copy> {
|
||||
}
|
||||
|
||||
// TODO: Generalize the return type of the weight function.
|
||||
// TODO: Add complexity information for Dijkstra's algorithm variants.
|
||||
// TODO: A Fibonacci heap would lower complexity to O(|E| + |V| log |V|) by making decrease-key O(1) amortized instead of O(log |V|). No standard Rust implementation exists; high constant factors may negate the asymptotic gain in practice.
|
||||
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances and predecessors.
|
||||
///
|
||||
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
|
||||
@@ -69,6 +53,8 @@ pub struct DijkstraResult<V: Copy> {
|
||||
/// of each vertex on some shortest path from `source` to that vertex. Returns `None` for any vertex
|
||||
/// not connected to `source`.
|
||||
///
|
||||
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `source` is not a valid vertex of `graph`.
|
||||
@@ -95,6 +81,7 @@ pub struct DijkstraResult<V: Copy> {
|
||||
pub fn dijkstra<G, W>(graph: &G, source: G::Vertex, weights: W) -> DijkstraResult<G::Vertex>
|
||||
where
|
||||
G: GraphTopology,
|
||||
G::Vertex: Hash,
|
||||
W: Fn(G::Edge) -> u32,
|
||||
{
|
||||
let mut predecessors = graph.vertex_map(None);
|
||||
@@ -113,6 +100,8 @@ where
|
||||
/// by `weights` function. Returns the distances from `source` to each vertex. Returns `None` for any
|
||||
/// vertex not connected to `source`.
|
||||
///
|
||||
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `source` is not a valid vertex of `graph`.
|
||||
@@ -142,6 +131,7 @@ pub fn dijkstra_distances<G, W>(
|
||||
) -> EntityMap<G::Vertex, Option<u32>>
|
||||
where
|
||||
G: GraphTopology,
|
||||
G::Vertex: Hash,
|
||||
W: Fn(G::Edge) -> u32,
|
||||
{
|
||||
dijkstra_impl(graph, source, weights, |_, _| {})
|
||||
@@ -153,6 +143,8 @@ where
|
||||
/// the distances from `source` to each vertex, and the predecessors of each vertex on some shortest
|
||||
/// path from `source` to that vertex. Returns `None` for any vertex not connected to `source`.
|
||||
///
|
||||
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
||||
///
|
||||
/// Prefer [`bfs`] for unweighted graphs, it has better time and space complexity.
|
||||
///
|
||||
/// # Panics
|
||||
@@ -179,6 +171,7 @@ where
|
||||
pub fn dijkstra_unweighted<G>(graph: &G, source: G::Vertex) -> DijkstraResult<G::Vertex>
|
||||
where
|
||||
G: GraphTopology,
|
||||
G::Vertex: Hash,
|
||||
{
|
||||
dijkstra(graph, source, |_| 1)
|
||||
}
|
||||
@@ -189,6 +182,8 @@ where
|
||||
/// the distances from `source` to each vertex. Returns `None` for any vertex not connected to
|
||||
/// `source`.
|
||||
///
|
||||
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
||||
///
|
||||
/// Prefer [`bfs_distances`] for unweighted graphs, it has better time and space complexity.
|
||||
///
|
||||
/// # Panics
|
||||
@@ -217,6 +212,7 @@ pub fn dijkstra_distances_unweighted<G>(
|
||||
) -> EntityMap<G::Vertex, Option<u32>>
|
||||
where
|
||||
G: GraphTopology,
|
||||
G::Vertex: Hash,
|
||||
{
|
||||
dijkstra_distances(graph, source, |_| 1)
|
||||
}
|
||||
@@ -229,32 +225,27 @@ fn dijkstra_impl<G, W, F>(
|
||||
) -> EntityMap<G::Vertex, Option<u32>>
|
||||
where
|
||||
G: GraphTopology,
|
||||
G::Vertex: Hash,
|
||||
W: Fn(G::Edge) -> u32,
|
||||
F: FnMut(G::Vertex, G::Vertex),
|
||||
{
|
||||
let mut distances = graph.vertex_map(None);
|
||||
let mut heap = BinaryHeap::new();
|
||||
let mut heap = PriorityQueue::new();
|
||||
|
||||
distances[source] = Some(0);
|
||||
heap.push(DistanceOrderedVertex {
|
||||
vertex: source,
|
||||
distance: 0,
|
||||
});
|
||||
heap.push(source, Reverse(0u32));
|
||||
|
||||
while let Some(v) = heap.pop() {
|
||||
for incidence in graph.incidences(v.vertex) {
|
||||
let new_distance = distances[v.vertex].unwrap() + weights(incidence.1);
|
||||
while let Some((v, Reverse(v_distance))) = heap.pop() {
|
||||
for incidence in graph.incidences(v) {
|
||||
let new_distance = v_distance + weights(incidence.1);
|
||||
if match distances[incidence.0] {
|
||||
None => true,
|
||||
Some(old_distance) if old_distance > new_distance => true,
|
||||
_ => false,
|
||||
} {
|
||||
distances[incidence.0] = Some(new_distance);
|
||||
on_relax(incidence.0, v.vertex);
|
||||
heap.push(DistanceOrderedVertex {
|
||||
vertex: incidence.0,
|
||||
distance: new_distance,
|
||||
});
|
||||
on_relax(incidence.0, v);
|
||||
heap.push_increase(incidence.0, Reverse(new_distance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user