diff --git a/src/main.rs b/src/main.rs index e839bf5..8217e97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +use std::cmp::Ordering; +use std::collections::BinaryHeap; + #[derive(Copy, Clone, PartialEq, Eq, Debug)] struct Vertex(usize); @@ -74,6 +77,70 @@ impl Graph { } } +#[derive(PartialEq, Eq)] +struct DistanceOrderedVertex { + distance: u32, + vertex: Vertex, +} + +impl PartialOrd for DistanceOrderedVertex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.distance.cmp(&other.distance)) + } +} + +impl Ord for DistanceOrderedVertex { + fn cmp(&self, other: &Self) -> Ordering { + other.distance.cmp(&self.distance) + } +} + +// TODO: Maybe introduce a return struct type for Dijkstra's algorithm? +fn dijkstra(graph: &Graph, source: Vertex) -> (Vec>, Vec>) { + let mut distances = vec![None; graph.vertex_count()]; + let mut predecessors = vec![None; graph.vertex_count()]; + let mut heap = BinaryHeap::new(); + + distances[source.0] = Some(0); + heap.push(DistanceOrderedVertex { + vertex: source, + distance: 0, + }); + while let Some(v) = heap.pop() { + // TODO: Simplify with a neighbor iterator. + // Finds the first neighbor of v. + let Some(mut incidence) = graph.incidence_headers[v.vertex.0].first_incidence else { + break; + }; + loop { + // Processes one neighbor of v. + let neighbor = graph.incidence_vertices[incidence.0]; + // TODO: Add a way to provide custom edge weights for Dijkstra's algorithm. + let edge_weight = 1; + let new_distance = distances[v.vertex.0].unwrap() + edge_weight; + if match distances[neighbor.0] { + None => true, + Some(old_distance) if old_distance > new_distance => true, + _ => false, + } { + distances[neighbor.0] = Some(new_distance); + predecessors[neighbor.0] = Some(v.vertex); + heap.push(DistanceOrderedVertex { + vertex: neighbor, + distance: new_distance, + }); + } + + // Finds next neighbor of v. + let Some(next) = graph.next_incidences[incidence.0] else { + break; + }; + incidence = next; + } + } + (distances, predecessors) +} + fn main() { // TODO: Move this graph example into one or more tests. let mut graph = Graph { @@ -154,6 +221,52 @@ fn main() { ); } - // TODO: test Dijkstra's algorithm starting at 0 and at another vertex. - let expected_distances_to_v0 = [0, 1, 2, 2, 2, 3, 3, 3, 3, 4]; + let (distances, predecessors) = dijkstra(&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), + ]; + let expected_predecessors_from_v0 = [ + vec![None], + vec![Some(vertices[0])], + vec![Some(vertices[1])], + vec![Some(vertices[1])], + vec![Some(vertices[1])], + vec![Some(vertices[2])], + vec![Some(vertices[2]), Some(vertices[3])], + vec![Some(vertices[4])], + vec![Some(vertices[4])], + vec![Some(vertices[5]), Some(vertices[6]), Some(vertices[7])], + ]; + assert_eq!( + distances.len(), + graph.vertex_count(), + "distances count from Dijkstra's algorithm must equal vertex count" + ); + assert_eq!( + predecessors.len(), + graph.vertex_count(), + "predecessors count from Dijkstra's algorithm must equal vertex count" + ); + for i in 0..graph.vertex_count() { + assert_eq!( + distances[i], expected_distances_from_v0[i], + "unexpected distance from {:?} to {:?} from Dijkstra's algorithm", + vertices[0], vertices[i] + ); + assert!( + expected_predecessors_from_v0[i].contains(&predecessors[i]), + "unexpected predecessor {:?} of {:?} from Dijkstra's algorithm", + predecessors[i], + vertices[i] + ); + } }