106 lines
3.0 KiB
Rust
106 lines
3.0 KiB
Rust
use std::{
|
|
collections::{HashMap, HashSet, VecDeque},
|
|
hash::Hash,
|
|
};
|
|
|
|
pub struct Graph<V> {
|
|
pub vertices: Vec<V>,
|
|
pub edges: HashMap<V, Vec<V>>,
|
|
}
|
|
|
|
impl<V> Graph<V>
|
|
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<Vec<V>> {
|
|
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::<Vec<_>>()
|
|
.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<Vec<V>> {
|
|
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::<Vec<_>>().into();
|
|
}
|
|
|
|
for neighbor in self.neighbors(current).iter().rev() {
|
|
if path.contains(&neighbor) {
|
|
continue;
|
|
}
|
|
|
|
path.push(neighbor);
|
|
q.push(path.clone());
|
|
path.pop();
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
}
|