add iter implementation

use HashSet for adjacency list instead of Vec
correct graph! macro for edge cases
This commit is contained in:
mxhagen 2025-02-16 20:38:29 +01:00
parent 14a3342f60
commit bd416fcf70
2 changed files with 72 additions and 8 deletions

View File

@ -3,14 +3,18 @@ use std::{
hash::Hash, hash::Hash,
}; };
pub trait VertexType: Eq + Hash + Ord + Clone {}
impl<T> VertexType for T where T: Eq + Hash + Ord + Clone {}
pub struct Graph<V> { pub struct Graph<V> {
pub vertices: HashSet<V>, pub vertices: HashSet<V>,
pub edges: HashMap<V, Vec<V>>, pub edges: HashMap<V, HashSet<V>>,
} }
impl<V> Graph<V> impl<V> Graph<V>
where where
V: Eq + Hash + Ord + Clone, V: VertexType,
{ {
/// get the number of vertices of the graph /// get the number of vertices of the graph
pub fn vertex_count(&self) -> usize { pub fn vertex_count(&self) -> usize {
@ -19,7 +23,7 @@ where
/// get the number of edges of the graph /// get the number of edges of the graph
pub fn edge_count(&self) -> usize { 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 /// check if an edge is contained in the graph
@ -43,10 +47,10 @@ where
} }
/// get a slice containing all neighbors of the edge /// 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 self.edges
.get(vertex) .get(vertex)
.map(Vec::as_slice) .map(|es| es.iter().collect())
.unwrap_or_default() .unwrap_or_default()
} }
@ -98,7 +102,7 @@ where
} }
for neighbor in self.neighbors(current).iter().rev() { for neighbor in self.neighbors(current).iter().rev() {
if path.contains(&neighbor) { if path.contains(neighbor) {
continue; continue;
} }
@ -110,6 +114,44 @@ where
None None
} }
pub fn iter(&self) -> impl Iterator<Item = &V> {
self.vertices.iter()
}
}
impl<V> IntoIterator for Graph<V>
where
V: VertexType,
{
type Item = V;
type IntoIter = IntoIter<V>;
fn into_iter(self) -> Self::IntoIter {
let items = self.vertices.into_iter().collect::<Vec<_>>().clone();
IntoIter { items, i: 0 }
}
}
pub struct IntoIter<V> {
items: Vec<V>,
i: usize,
}
impl<V> Iterator for IntoIter<V>
where
V: VertexType,
{
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
match self.items.get(self.i) {
None => None,
item => {
self.i += 1;
item.cloned()
}
}
}
} }
#[macro_export] #[macro_export]
@ -119,8 +161,14 @@ macro_rules! graph {
let mut vertices = std::collections::HashSet::new(); let mut vertices = std::collections::HashSet::new();
$( $(
vertices.insert($v); vertices.insert($v); // insert vertex
edges.insert($v, vec![$( $e ),*]); 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); )* $( vertices.insert($e); )*
)* )*

View File

@ -1,3 +1,5 @@
use std::collections::HashSet;
mod graph; mod graph;
pub use graph::Graph; pub use graph::Graph;
@ -7,6 +9,7 @@ fn main() {
// -> 0 ┃ 3 // -> 0 ┃ 3
// ┗━━━━━2━━━━━┛ // ┗━━━━━2━━━━━┛
// adjacency list
let g = graph! { let g = graph! {
0: 1, 2; 0: 1, 2;
1: 0, 2, 3; 1: 0, 2, 3;
@ -14,10 +17,13 @@ fn main() {
3: 1, 2; 3: 1, 2;
}; };
// vertices
for v in &g.vertices { for v in &g.vertices {
assert!(g.has_vertex(v), "G should contain vertex {v}"); assert!(g.has_vertex(v), "G should contain vertex {v}");
} }
// edges
for (a, neighbors) in &g.edges { for (a, neighbors) in &g.edges {
for b in neighbors { for b in neighbors {
assert!(g.has_edge((a, b)), "G should contain edge ({a}, {b})"); 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 n = g.vertex_count();
let vs = g.vertices.iter().collect::<Vec<_>>(); let vs = g.vertices.iter().collect::<Vec<_>>();
for i in 0..n { 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::<HashSet<_>>();
assert_eq!(seen, all_vs, "Iterating G should yield all vertices");
// yay
println!("All tests passed."); println!("All tests passed.");
} }