use std::{ collections::{HashMap, HashSet, VecDeque}, hash::Hash, }; pub struct Graph { pub vertices: Vec, pub edges: HashMap>, } impl Graph where V: Eq + Hash + Ord + Clone, { /// check if an edge is contained in the graph /// (only checks the provided direction) pub fn has_edge(&self, edge: (&V, &V)) -> bool { let (from, to) = edge; self.edges .get(from) .map_or(false, |v| v.iter().any(|x| x == to)) } /// check if an edge (a, b) and its' reverse (b, a) are both contained in the graph pub fn has_bidirectional_edge(&self, edge: (&V, &V)) -> bool { let (a, b) = edge; self.has_edge((a, b)) && self.has_edge((b, a)) } /// check if a vertex is contained in the graph pub fn has_vertex(&self, vertex: &V) -> bool { self.vertices.contains(vertex) } /// get a slice containing all neighbors of the edge pub fn neighbors(&self, vertex: &V) -> &[V] { self.edges .get(vertex) .map(Vec::as_slice) .unwrap_or_default() } /// find a path between two nodes using breadth-first-search. /// this finds a path with the least possible edges. pub fn find_path_bfs(&self, from: &V, to: &V) -> Option> { let mut q = VecDeque::with_capacity(self.vertices.len()); let mut visited = HashSet::with_capacity(self.vertices.len()); q.push_back(vec![from]); visited.insert(from); while !q.is_empty() { let mut path = q.pop_front().unwrap(); let current = path.last().unwrap(); for neighbor in self.neighbors(current) { if neighbor == to { return path .into_iter() .cloned() .chain([to.clone()]) .collect::>() .into(); } path.push(neighbor); q.push_back(path.clone()); path.pop(); } } None } /// find a path between two nodes using depth-first-search. /// /// this is short-circuiting and therefore does not guarantee /// the found path to contain the least possible edges. pub fn find_path_dfs(&self, from: &V, to: &V) -> Option> { let mut q = Vec::with_capacity(self.vertices.len()); q.push(vec![from]); while !q.is_empty() { let mut path = q.pop().unwrap(); let ¤t = path.last().unwrap(); if current == to { return path.into_iter().cloned().collect::>().into(); } for neighbor in self.neighbors(current).iter().rev() { if path.contains(&neighbor) { continue; } path.push(neighbor); q.push(path.clone()); path.pop(); } } None } }