diff --git a/src/algorithms.rs b/src/algorithms.rs index 7b65faf..b8282e4 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -26,20 +26,37 @@ pub struct DijkstraResult { pub predecessors: Vec>, } -// TODO: Maybe variant of Dijkstra's algorithm without predecessors? -// TODO: Replace Vec storage with VertexMap, this should make the bound on G::Vertex here obsolete. pub fn dijkstra(graph: &G, source: G::Vertex) -> DijkstraResult where G: GraphTopology, G::Vertex: Into, { - let mut result = DijkstraResult { - distances: vec![None; graph.vertex_count()], - predecessors: vec![None; graph.vertex_count()], - }; + let mut predecessors = vec![None; graph.vertex_count()]; + let distances = dijkstra_impl(graph, source, |neighbor, predecessor| { + predecessors[neighbor.into()] = Some(predecessor); + }); + DijkstraResult { distances, predecessors } +} + +pub fn dijkstra_distances(graph: &G, source: G::Vertex) -> Vec> +where + G: GraphTopology, + G::Vertex: Into, +{ + dijkstra_impl(graph, source, |_, _| {}) +} + +// TODO: Replace Vec storage with VertexMap, this should make the bound on G::Vertex here obsolete. +fn dijkstra_impl(graph: &G, source: G::Vertex, mut on_relax: F) -> Vec> +where + G: GraphTopology, + G::Vertex: Into, + F: FnMut(G::Vertex, G::Vertex), +{ + let mut distances = vec![None; graph.vertex_count()]; let mut heap = BinaryHeap::new(); - result.distances[source.into()] = Some(0); + distances[source.into()] = Some(0); heap.push(DistanceOrderedVertex { vertex: source, distance: 0, @@ -49,14 +66,14 @@ where for neighbor in graph.neighbors(v.vertex) { // TODO: Add a way to provide custom edge weights for Dijkstra's algorithm. let edge_weight = 1; - let new_distance = result.distances[v.vertex.into()].unwrap() + edge_weight; - if match result.distances[neighbor.into()] { + let new_distance = distances[v.vertex.into()].unwrap() + edge_weight; + if match distances[neighbor.into()] { None => true, Some(old_distance) if old_distance > new_distance => true, _ => false, } { - result.distances[neighbor.into()] = Some(new_distance); - result.predecessors[neighbor.into()] = Some(v.vertex); + distances[neighbor.into()] = Some(new_distance); + on_relax(neighbor, v.vertex); heap.push(DistanceOrderedVertex { vertex: neighbor, distance: new_distance, @@ -64,5 +81,5 @@ where } } } - result + distances } diff --git a/tests/dijkstra.rs b/tests/dijkstra.rs index f4dc9ad..76383a4 100644 --- a/tests/dijkstra.rs +++ b/tests/dijkstra.rs @@ -107,6 +107,55 @@ fn dijkstra() { } } +#[test] +fn dijkstra_distances_single_vertex() { + let mut graph = AppendGraph::new(); + let v1 = graph.add_vertex(); + let distances = algorithms::dijkstra_distances(&graph, v1); + assert_eq!(distances.len(), 1, "distances count must equal vertex count"); + assert_eq!(distances[0], Some(0), "unexpected distance of source vertex"); +} + +#[test] +fn dijkstra_distances_disconnected() { + let mut graph = AppendGraph::new(); + let v1 = graph.add_vertex(); + graph.add_vertex(); + let distances = algorithms::dijkstra_distances(&graph, v1); + assert_eq!(distances.len(), 2, "distances count must equal vertex count"); + assert_eq!(distances[1], None, "unexpected distance of disconnected vertex"); +} + +#[test] +fn dijkstra_distances() { + let (graph, vertices) = make_test_graph(); + let distances = algorithms::dijkstra_distances(&graph, vertices[0]); + let expected_distances_from_v0 = [ + Some(0), + Some(1), + Some(2), + Some(2), + Some(2), + Some(3), + Some(3), + Some(3), + Some(3), + Some(4), + ]; + assert_eq!( + distances.len(), + graph.vertex_count(), + "distances count must equal vertex count" + ); + for i in 0..graph.vertex_count() { + assert_eq!( + distances[i], expected_distances_from_v0[i], + "unexpected distance from {:?} to {:?}", + vertices[0], vertices[i] + ); + } +} + fn make_test_graph() -> (AppendGraph, [::Vertex; 10]) { let mut graph = AppendGraph::new(); let vertices = core::array::from_fn::<_, 10, _>(|_| graph.add_vertex());