Use PriorityQueue for Dijkstra's, requires Hash for Vertex

This commit is contained in:
2026-08-06 09:44:28 +02:00
parent 53a6bed932
commit 110e6310aa
3 changed files with 28 additions and 36 deletions
+1
View File
@@ -11,4 +11,5 @@ keywords = ["graph", "graph-algorithms"]
categories = ["algorithms", "data-structures", "mathematics"]
[dependencies]
priority-queue = "2.7.0"
typed-generational-arena = "0.2.9"
+25 -34
View File
@@ -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));
}
}
}
+2 -2
View File
@@ -7,14 +7,14 @@ use crate::traits::{GraphTopology, IncidenceCursor};
///
/// Handles are stable for the lifetime of the graph. Obtain via graph methods like
/// [`AppendGraph::add_vertex`] and [`AppendGraph::vertices`].
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct Vertex(usize);
/// An opaque handle identifying an edge in an [`AppendGraph`].
///
/// Handles are stable for the lifetime of the graph. Obtain via graph methods like
/// [`AppendGraph::add_edge`] and [`AppendGraph::edges`].
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct Edge(usize);
/// An [`EntityMap`] for [`AppendGraph`] vertices.