use std::cmp::Reverse; use std::collections::BinaryHeap; /// Computes shortest distances from `source` in a directed, weighted graph. /// /// `graph[u]` contains `(v, weight)` edges from `u` to `v`. /// Unreachable vertices have distance `None`. pub fn dijkstra(graph: &[Vec<(usize, u64)>], source: usize) -> Vec> { let mut distances = vec![None; graph.len()]; if source >= graph.len() { return distances; } // The heap stores the smallest tentative distance first. let mut heap = BinaryHeap::new(); distances[source] = Some(0); heap.push(Reverse((0_u64, source))); while let Some(Reverse((distance, vertex))) = heap.pop() { // Ignore entries made obsolete by a shorter path. if distances[vertex] != Some(distance) { continue; } for &(neighbor, weight) in &graph[vertex] { assert!(neighbor < graph.len(), "edge points outside the graph"); // Ignore paths whose total weight cannot fit in u64. let Some(candidate) = distance.checked_add(weight) else { continue; }; if distances[neighbor].is_none_or(|current| candidate < current) { distances[neighbor] = Some(candidate); heap.push(Reverse((candidate, neighbor))); } } } distances } #[cfg(test)] mod tests { use super::dijkstra; #[test] fn finds_shortest_paths() { let graph = vec![ vec![(1, 4), (2, 1)], vec![(3, 1)], vec![(1, 2), (3, 5)], vec![], ]; assert_eq!( dijkstra(&graph, 0), vec![Some(0), Some(3), Some(1), Some(4)] ); } #[test] fn leaves_unreachable_vertices_unset() { let graph = vec![vec![(1, 7)], vec![], vec![]]; assert_eq!(dijkstra(&graph, 0), vec![Some(0), Some(7), None]); } }