From bd416fcf700c9b7d6b2c254cdae6036b904b9c1a Mon Sep 17 00:00:00 2001 From: mxhagen Date: Sun, 16 Feb 2025 20:38:29 +0100 Subject: [PATCH] add iter implementation use HashSet for adjacency list instead of Vec correct graph! macro for edge cases --- src/graph.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++------- src/main.rs | 16 +++++++++++++ 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 6a6407d..ee64bba 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -3,14 +3,18 @@ use std::{ hash::Hash, }; +pub trait VertexType: Eq + Hash + Ord + Clone {} + +impl VertexType for T where T: Eq + Hash + Ord + Clone {} + pub struct Graph { pub vertices: HashSet, - pub edges: HashMap>, + pub edges: HashMap>, } impl Graph where - V: Eq + Hash + Ord + Clone, + V: VertexType, { /// get the number of vertices of the graph pub fn vertex_count(&self) -> usize { @@ -19,7 +23,7 @@ where /// get the number of edges of the graph pub fn edge_count(&self) -> usize { - self.edges.values().map(Vec::len).sum() + self.edges.values().map(HashSet::len).sum() } /// check if an edge is contained in the graph @@ -43,10 +47,10 @@ where } /// get a slice containing all neighbors of the edge - pub fn neighbors(&self, vertex: &V) -> &[V] { + pub fn neighbors(&self, vertex: &V) -> Vec<&V> { self.edges .get(vertex) - .map(Vec::as_slice) + .map(|es| es.iter().collect()) .unwrap_or_default() } @@ -98,7 +102,7 @@ where } for neighbor in self.neighbors(current).iter().rev() { - if path.contains(&neighbor) { + if path.contains(neighbor) { continue; } @@ -110,6 +114,44 @@ where None } + + pub fn iter(&self) -> impl Iterator { + self.vertices.iter() + } +} + +impl IntoIterator for Graph +where + V: VertexType, +{ + type Item = V; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + let items = self.vertices.into_iter().collect::>().clone(); + IntoIter { items, i: 0 } + } +} + +pub struct IntoIter { + items: Vec, + i: usize, +} + +impl Iterator for IntoIter +where + V: VertexType, +{ + type Item = V; + fn next(&mut self) -> Option { + match self.items.get(self.i) { + None => None, + item => { + self.i += 1; + item.cloned() + } + } + } } #[macro_export] @@ -119,8 +161,14 @@ macro_rules! graph { let mut vertices = std::collections::HashSet::new(); $( - vertices.insert($v); - edges.insert($v, vec![$( $e ),*]); + vertices.insert($v); // insert vertex + let v_neighbors = std::collections::HashSet::from([ + $( $e ),* + ]); + + edges.entry($v).or_insert(v_neighbors); // initialize its edges + + // insert vertices with no outgoing edges $( vertices.insert($e); )* )* diff --git a/src/main.rs b/src/main.rs index d2f4d9d..acbd595 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + mod graph; pub use graph::Graph; @@ -7,6 +9,7 @@ fn main() { // -> 0 ┃ 3 // ┗━━━━━2━━━━━┛ + // adjacency list let g = graph! { 0: 1, 2; 1: 0, 2, 3; @@ -14,10 +17,13 @@ fn main() { 3: 1, 2; }; + // vertices for v in &g.vertices { assert!(g.has_vertex(v), "G should contain vertex {v}"); } + + // edges for (a, neighbors) in &g.edges { for b in neighbors { assert!(g.has_edge((a, b)), "G should contain edge ({a}, {b})"); @@ -28,6 +34,8 @@ fn main() { } } + + // dfs, bfs let n = g.vertex_count(); let vs = g.vertices.iter().collect::>(); for i in 0..n { @@ -58,5 +66,13 @@ fn main() { } } + + // iterator implementation + let all_vs = g.vertices.clone(); + let seen = g.into_iter().collect::>(); + assert_eq!(seen, all_vs, "Iterating G should yield all vertices"); + + + // yay println!("All tests passed."); }