Add algorithms::bfs_find_path

This commit is contained in:
2026-09-03 09:43:45 +02:00
parent a8b6f7591c
commit 73af8a16ca
2 changed files with 223 additions and 3 deletions
+109 -3
View File
@@ -425,10 +425,8 @@ where
F: FnMut(G::Vertex, G::Vertex) -> bool,
{
let mut distances = graph.vertex_map(None);
let mut queue = VecDeque::new();
distances[source] = Some(0);
queue.push_back(source);
let mut queue = VecDeque::from([source]);
while let Some(v) = queue.pop_front() {
for neighbor in graph.adjacent_vertices(v) {
@@ -451,6 +449,114 @@ where
}
}
/// [Breadth-first search] from `source` for `target`, returns some shortest path as a sequence of
/// edges.
///
/// Traverses vertices in `graph` starting from `source` until `target` is found, exploring all
/// neighbors before descending further. Returns some shortest path from `source` to `target` as a
/// sequence of edges in traversal order if `target` is a valid vertex of `graph` connected to
/// `source`, or `None` otherwise. The returned path is empty if and only if `source` equals
/// `target`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::bfs_find_path;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// let e = graph.add_edge(source, target);
///
/// assert_eq!(bfs_find_path(&graph, source, target), Some(vec![e]));
/// ```
///
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
pub fn bfs_find_path<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<Vec<G::Edge>>
where
G: GraphTopology,
{
bfs_find_path_where(graph, source, |v| v == target)
}
/// [Breadth-first search] from `source` for a vertex matching `predicate`, returns some shortest
/// path as a sequence of edges.
///
/// Traverses vertices in `graph` starting from `source` until a vertex satisfying `predicate` is
/// found, exploring all neighbors before descending further. Returns some shortest path from
/// `source` to the first vertex satisfying `predicate` as a sequence of edges in traversal order,
/// or `None` if no such vertex is connected to `source`. The returned path is empty if and only if
/// `source` satisfies `predicate`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::bfs_find_path_where;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// let e = graph.add_edge(source, target);
///
/// assert_eq!(bfs_find_path_where(&graph, source, |v| v == target), Some(vec![e]));
/// ```
///
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
pub fn bfs_find_path_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<Vec<G::Edge>>
where
G: GraphTopology,
P: Fn(G::Vertex) -> bool,
{
if predicate(source) {
return Some(vec![]);
}
let mut visited = graph.vertex_map(false);
visited[source] = true;
let mut predecessor: ElementMap<G::Vertex, Option<(G::Vertex, G::Edge)>> =
graph.vertex_map(None);
let mut queue = VecDeque::from([source]);
while let Some(v) = queue.pop_front() {
for Incidence {
vertex: neighbor,
edge,
} in graph.incidences(v)
{
if !visited[neighbor] {
visited[neighbor] = true;
predecessor[neighbor] = Some((v, edge));
if predicate(neighbor) {
let mut path = vec![edge];
let mut current = v;
while let Some((prev, e)) = predecessor[current] {
path.push(e);
current = prev;
}
path.reverse();
return Some(path);
}
queue.push_back(neighbor);
}
}
}
None
}
// TODO: 'visited' is already encoded in 'predecessors', except for 'source', which is visited, but has no predecessor.
/// Return data type for [`dfs`].
pub struct DfsResult<V: Copy> {