763 lines
25 KiB
Rust
763 lines
25 KiB
Rust
//! Algorithms for graph topologies.
|
|
//!
|
|
//! # Dijkstra's algorithm
|
|
//!
|
|
//! [Dijkstra's algorithm] finds the shortest distances from a source vertex to all other vertices
|
|
//! in the graph. Note that for unweighted graphs it has worse time and space complexity than
|
|
//! [Breadth-first search](#breadth-first-search).
|
|
//!
|
|
//! Variants: [`dijkstra`], [`dijkstra_distances`], [`dijkstra_unweighted`],
|
|
//! [`dijkstra_distances_unweighted`].
|
|
//!
|
|
//! # Breadth-first search
|
|
//!
|
|
//! [Breadth-first search] traverses a graph topology, exploring all neighboring vertices first
|
|
//! before descending further into their neighborhoods.
|
|
//!
|
|
//! Variants: [`bfs`], [`bfs_distances`], [`bfs_find`], [`bfs_find_where`].
|
|
//!
|
|
//! # Depth-first search
|
|
//!
|
|
//! [Depth-first search] traverses a graph topology, exploring each branch path as far as possible
|
|
//! first before backtracking and exploring other branches.
|
|
//!
|
|
//! Variants: [`dfs`], [`dfs_visited`], [`dfs_find`], [`dfs_find_where`], [`dfs_find_path`],
|
|
//! [`dfs_find_path_where`].
|
|
//!
|
|
//! [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
|
|
//! [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
|
//! [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
|
|
|
use priority_queue::PriorityQueue;
|
|
use std::cmp::Reverse;
|
|
use std::collections::VecDeque;
|
|
use std::hash::Hash;
|
|
|
|
use crate::maps::EntityMap;
|
|
use crate::traits::{GraphTopology, IncidenceCursor};
|
|
|
|
/// Return data type for [`dijkstra`] and [`dijkstra_unweighted`].
|
|
pub struct DijkstraResult<V: Copy> {
|
|
/// Vertex map of minimum distances from a given `source` vertex.
|
|
pub distances: EntityMap<V, Option<u32>>,
|
|
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
|
pub predecessors: EntityMap<V, Option<V>>,
|
|
}
|
|
|
|
// TODO: Generalize the return type of the weight function.
|
|
// TODO: A Fibonacci heap would lower complexity to O(|E| + |V| log |V|) by making decrease-key O(1) amortized instead of O(log |V|). No standard Rust implementation exists; high constant factors may negate the asymptotic gain in practice.
|
|
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances and predecessors.
|
|
///
|
|
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
|
|
/// by `weights` function. Returns the distances from `source` to each vertex, and the predecessors
|
|
/// of each vertex on some shortest path from `source` to that vertex. Returns `None` for any vertex
|
|
/// not connected to `source`.
|
|
///
|
|
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if `source` is not a valid vertex of `graph`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use grapherity::prelude::*;
|
|
/// # use grapherity::algorithms::dijkstra;
|
|
/// # 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);
|
|
/// let mut weights = graph.edge_map(1);
|
|
/// weights[e] = 5;
|
|
///
|
|
/// let result = dijkstra(&graph, source, |e| weights[e]);
|
|
/// assert_eq!(result.distances[target], Some(5));
|
|
/// assert_eq!(result.predecessors[target], Some(source));
|
|
/// ```
|
|
///
|
|
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
|
pub fn dijkstra<G, W>(graph: &G, source: G::Vertex, weights: W) -> DijkstraResult<G::Vertex>
|
|
where
|
|
G: GraphTopology,
|
|
G::Vertex: Hash,
|
|
W: Fn(G::Edge) -> u32,
|
|
{
|
|
let mut predecessors = graph.vertex_map(None);
|
|
let distances = dijkstra_impl(graph, source, weights, |adjacent, predecessor| {
|
|
predecessors[adjacent] = Some(predecessor);
|
|
});
|
|
DijkstraResult {
|
|
distances,
|
|
predecessors,
|
|
}
|
|
}
|
|
|
|
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances.
|
|
///
|
|
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
|
|
/// by `weights` function. Returns the distances from `source` to each vertex. Returns `None` for any
|
|
/// vertex not connected to `source`.
|
|
///
|
|
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if `source` is not a valid vertex of `graph`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use grapherity::prelude::*;
|
|
/// # use grapherity::algorithms::dijkstra_distances;
|
|
/// # 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);
|
|
/// let mut weights = graph.edge_map(1);
|
|
/// weights[e] = 5;
|
|
///
|
|
/// let distances = dijkstra_distances(&graph, source, |e| weights[e]);
|
|
/// assert_eq!(distances[target], Some(5));
|
|
/// ```
|
|
///
|
|
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
|
pub fn dijkstra_distances<G, W>(
|
|
graph: &G,
|
|
source: G::Vertex,
|
|
weights: W,
|
|
) -> EntityMap<G::Vertex, Option<u32>>
|
|
where
|
|
G: GraphTopology,
|
|
G::Vertex: Hash,
|
|
W: Fn(G::Edge) -> u32,
|
|
{
|
|
dijkstra_impl(graph, source, weights, |_, _| {})
|
|
}
|
|
|
|
/// [Dijkstra's algorithm] with unit edge weights, returns minimum distances and predecessors.
|
|
///
|
|
/// Calculates the shortest paths from `source` to all vertices in an unweighted `graph`. Returns
|
|
/// the distances from `source` to each vertex, and the predecessors of each vertex on some shortest
|
|
/// path from `source` to that vertex. Returns `None` for any vertex not connected to `source`.
|
|
///
|
|
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
|
///
|
|
/// Prefer [`bfs`] for unweighted graphs, it has better time and space complexity.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if `source` is not a valid vertex of `graph`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use grapherity::prelude::*;
|
|
/// # use grapherity::algorithms::dijkstra_unweighted;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// let result = dijkstra_unweighted(&graph, source);
|
|
/// assert_eq!(result.distances[target], Some(1));
|
|
/// assert_eq!(result.predecessors[target], Some(source));
|
|
/// ```
|
|
///
|
|
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
|
pub fn dijkstra_unweighted<G>(graph: &G, source: G::Vertex) -> DijkstraResult<G::Vertex>
|
|
where
|
|
G: GraphTopology,
|
|
G::Vertex: Hash,
|
|
{
|
|
dijkstra(graph, source, |_| 1)
|
|
}
|
|
|
|
/// [Dijkstra's algorithm] with unit edge weights, returns minimum distances.
|
|
///
|
|
/// Calculates the shortest paths from `source` to all vertices in an unweighted `graph`. Returns
|
|
/// the distances from `source` to each vertex. Returns `None` for any vertex not connected to
|
|
/// `source`.
|
|
///
|
|
/// Time complexity is *O((|V| + |E|) log |V|)*, space complexity is *O(|V|)*.
|
|
///
|
|
/// Prefer [`bfs_distances`] for unweighted graphs, it has better time and space complexity.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if `source` is not a valid vertex of `graph`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use grapherity::prelude::*;
|
|
/// # use grapherity::algorithms::dijkstra_distances_unweighted;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// let distances = dijkstra_distances_unweighted(&graph, source);
|
|
/// assert_eq!(distances[target], Some(1));
|
|
/// ```
|
|
///
|
|
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
|
pub fn dijkstra_distances_unweighted<G>(
|
|
graph: &G,
|
|
source: G::Vertex,
|
|
) -> EntityMap<G::Vertex, Option<u32>>
|
|
where
|
|
G: GraphTopology,
|
|
G::Vertex: Hash,
|
|
{
|
|
dijkstra_distances(graph, source, |_| 1)
|
|
}
|
|
|
|
fn dijkstra_impl<G, W, F>(
|
|
graph: &G,
|
|
source: G::Vertex,
|
|
weights: W,
|
|
mut on_relax: F,
|
|
) -> EntityMap<G::Vertex, Option<u32>>
|
|
where
|
|
G: GraphTopology,
|
|
G::Vertex: Hash,
|
|
W: Fn(G::Edge) -> u32,
|
|
F: FnMut(G::Vertex, G::Vertex),
|
|
{
|
|
let mut distances = graph.vertex_map(None);
|
|
let mut heap = PriorityQueue::new();
|
|
|
|
distances[source] = Some(0);
|
|
heap.push(source, Reverse(0u32));
|
|
|
|
while let Some((v, Reverse(v_distance))) = heap.pop() {
|
|
for incidence in graph.incidences(v) {
|
|
let new_distance = v_distance + weights(incidence.1);
|
|
if match distances[incidence.0] {
|
|
None => true,
|
|
Some(old_distance) if old_distance > new_distance => true,
|
|
_ => false,
|
|
} {
|
|
distances[incidence.0] = Some(new_distance);
|
|
on_relax(incidence.0, v);
|
|
heap.push_increase(incidence.0, Reverse(new_distance));
|
|
}
|
|
}
|
|
}
|
|
distances
|
|
}
|
|
|
|
/// Return data type for [`bfs`].
|
|
pub struct BfsResult<V: Copy> {
|
|
/// Vertex map of minimum distances from a given `source` vertex.
|
|
pub distances: EntityMap<V, Option<u32>>,
|
|
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
|
pub predecessors: EntityMap<V, Option<V>>,
|
|
}
|
|
|
|
/// [Breadth-first search] traversal from `source`, returns distances and predecessors.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source`, exploring all neighbors before descending
|
|
/// further. Returns the distances from `source` to each vertex, and the predecessors of each vertex
|
|
/// on some shortest path from `source` to that vertex. Returns `None` for any vertex not connected
|
|
/// to `source`.
|
|
///
|
|
/// 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;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// let result = bfs(&graph, source);
|
|
/// assert_eq!(result.distances[target], Some(1));
|
|
/// assert_eq!(result.predecessors[target], Some(source));
|
|
/// ```
|
|
///
|
|
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
|
|
pub fn bfs<G>(graph: &G, source: G::Vertex) -> BfsResult<G::Vertex>
|
|
where
|
|
G: GraphTopology,
|
|
{
|
|
let mut predecessors = graph.vertex_map(None);
|
|
let result = bfs_impl(graph, source, |neighbor, predecessor| {
|
|
predecessors[neighbor] = Some(predecessor);
|
|
true
|
|
});
|
|
BfsResult {
|
|
distances: result.distances,
|
|
predecessors,
|
|
}
|
|
}
|
|
|
|
/// [Breadth-first search] traversal from `source`, returns distances.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source`, exploring all neighbors before descending
|
|
/// further. Returns the distances from `source` to each vertex. Returns `None` for any vertex not
|
|
/// connected to `source`.
|
|
///
|
|
/// 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_distances;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// let distances = bfs_distances(&graph, source);
|
|
/// assert_eq!(distances[target], Some(1));
|
|
/// ```
|
|
///
|
|
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
|
|
pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> EntityMap<G::Vertex, Option<u32>>
|
|
where
|
|
G: GraphTopology,
|
|
{
|
|
bfs_impl(graph, source, |_, _| true).distances
|
|
}
|
|
|
|
/// [Breadth-first search] from `source` for `target`, returns the distance.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source` until `target` is found, exploring all
|
|
/// neighbors before descending further. Returns the minimum distance from `source` to `target` if
|
|
/// `target` is a valid vertex of `graph` connected to `source`, or `None` otherwise.
|
|
///
|
|
/// 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;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// assert_eq!(bfs_find(&graph, source, target), Some(1));
|
|
/// ```
|
|
///
|
|
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
|
|
pub fn bfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<u32>
|
|
where
|
|
G: GraphTopology,
|
|
{
|
|
bfs_find_where(graph, source, |v| v == target).map(|(_, distance)| distance)
|
|
}
|
|
|
|
/// [Breadth-first search] from `source` for a vertex matching `predicate`, returns the vertex and
|
|
/// its distance.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source` until a vertex satisfying `predicate` is
|
|
/// found, exploring all neighbors before descending further. Returns the first vertex satisfying
|
|
/// `predicate` and its minimum distance from `source`, or `None` if no such vertex is connected to
|
|
/// `source`.
|
|
///
|
|
/// 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_where;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// assert_eq!(bfs_find_where(&graph, source, |v| v == target), Some((target, 1)));
|
|
/// ```
|
|
///
|
|
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
|
|
pub fn bfs_find_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<(G::Vertex, u32)>
|
|
where
|
|
G: GraphTopology,
|
|
P: Fn(G::Vertex) -> bool,
|
|
{
|
|
if predicate(source) {
|
|
return Some((source, 0));
|
|
}
|
|
bfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
|
|
}
|
|
|
|
struct BfsImplResult<V: Copy> {
|
|
distances: EntityMap<V, Option<u32>>,
|
|
found: Option<(V, u32)>,
|
|
}
|
|
|
|
fn bfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> BfsImplResult<G::Vertex>
|
|
where
|
|
G: GraphTopology,
|
|
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);
|
|
|
|
while let Some(v) = queue.pop_front() {
|
|
for neighbor in graph.adjacent_vertices(v) {
|
|
if distances[neighbor].is_none() {
|
|
let distance = distances[v].unwrap() + 1;
|
|
distances[neighbor] = Some(distance);
|
|
if !on_discover(neighbor, v) {
|
|
return BfsImplResult {
|
|
distances,
|
|
found: Some((neighbor, distance)),
|
|
};
|
|
}
|
|
queue.push_back(neighbor);
|
|
}
|
|
}
|
|
}
|
|
BfsImplResult {
|
|
distances,
|
|
found: 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> {
|
|
/// Vertex map indicating which vertices were visited during the search.
|
|
pub visited: EntityMap<V, bool>,
|
|
/// Vertex map of predecessors on the DFS tree path from a given `source` vertex.
|
|
pub predecessors: EntityMap<V, Option<V>>,
|
|
}
|
|
|
|
/// [Depth-first search] traversal from `source`, returns visited vertices and predecessors.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source`, exploring each branch as far as possible
|
|
/// before backtracking. Returns for each vertex whether it was visited, and the predecessors of
|
|
/// each vertex on the DFS tree path from `source` to that vertex. Any vertex not connected to
|
|
/// `source` is marked unvisited and has no predecessor.
|
|
///
|
|
/// 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::dfs;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// let result = dfs(&graph, source);
|
|
/// assert!(result.visited[target]);
|
|
/// assert_eq!(result.predecessors[target], Some(source));
|
|
/// ```
|
|
///
|
|
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
|
pub fn dfs<G>(graph: &G, source: G::Vertex) -> DfsResult<G::Vertex>
|
|
where
|
|
G: GraphTopology,
|
|
{
|
|
let mut predecessors = graph.vertex_map(None);
|
|
let result = dfs_impl(graph, source, |neighbor, predecessor| {
|
|
predecessors[neighbor] = Some(predecessor);
|
|
true
|
|
});
|
|
DfsResult {
|
|
visited: result.visited,
|
|
predecessors,
|
|
}
|
|
}
|
|
|
|
/// [Depth-first search] traversal from `source`, returns visited vertices.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source`, exploring each branch as far as possible
|
|
/// before backtracking. Returns a map indicating which vertices were visited, i.e. which vertices
|
|
/// are connected to `source`.
|
|
///
|
|
/// 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::dfs_visited;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// let visited = dfs_visited(&graph, source);
|
|
/// assert!(visited[target]);
|
|
/// ```
|
|
///
|
|
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
|
pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> EntityMap<G::Vertex, bool>
|
|
where
|
|
G: GraphTopology,
|
|
{
|
|
dfs_impl(graph, source, |_, _| true).visited
|
|
}
|
|
|
|
/// [Depth-first search] from `source` for `target`, returns whether it was found.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source` until `target` is found, exploring each
|
|
/// branch as far as possible before backtracking. Returns `true` if `target` is a valid vertex of
|
|
/// `graph` connected to `source`, or `false` otherwise.
|
|
///
|
|
/// 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::dfs_find;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// assert!(dfs_find(&graph, source, target));
|
|
/// ```
|
|
///
|
|
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
|
pub fn dfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> bool
|
|
where
|
|
G: GraphTopology,
|
|
{
|
|
dfs_find_where(graph, source, |v| v == target).is_some()
|
|
}
|
|
|
|
/// [Depth-first search] from `source` for a vertex matching `predicate`, returns the vertex.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source` until a vertex satisfying `predicate` is
|
|
/// found, exploring each branch as far as possible before backtracking. Returns the first vertex
|
|
/// satisfying `predicate`, or `None` if no such vertex is connected to `source`.
|
|
///
|
|
/// 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::dfs_find_where;
|
|
/// # use grapherity::models::Graph;
|
|
/// let mut graph = Graph::new();
|
|
/// let source = graph.add_vertex();
|
|
/// let target = graph.add_vertex();
|
|
/// graph.add_edge(source, target);
|
|
///
|
|
/// assert_eq!(dfs_find_where(&graph, source, |v| v == target), Some(target));
|
|
/// ```
|
|
///
|
|
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
|
pub fn dfs_find_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<G::Vertex>
|
|
where
|
|
G: GraphTopology,
|
|
P: Fn(G::Vertex) -> bool,
|
|
{
|
|
if predicate(source) {
|
|
return Some(source);
|
|
}
|
|
dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
|
|
}
|
|
|
|
struct DfsImplResult<V: Copy> {
|
|
visited: EntityMap<V, bool>,
|
|
found: Option<V>,
|
|
}
|
|
|
|
fn dfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> DfsImplResult<G::Vertex>
|
|
where
|
|
G: GraphTopology,
|
|
F: FnMut(G::Vertex, G::Vertex) -> bool,
|
|
{
|
|
let mut visited = graph.vertex_map(false);
|
|
visited[source] = true;
|
|
let mut stack = vec![(source, None::<G::Vertex>)];
|
|
|
|
while let Some((v, predecessor)) = stack.pop() {
|
|
if let Some(p) = predecessor {
|
|
if !on_discover(v, p) {
|
|
return DfsImplResult {
|
|
visited,
|
|
found: Some(v),
|
|
};
|
|
}
|
|
}
|
|
for neighbor in graph.adjacent_vertices(v) {
|
|
if !visited[neighbor] {
|
|
visited[neighbor] = true;
|
|
stack.push((neighbor, Some(v)));
|
|
}
|
|
}
|
|
}
|
|
DfsImplResult {
|
|
visited,
|
|
found: None,
|
|
}
|
|
}
|
|
|
|
/// [Depth-first search] from `source` for `target`, returns the path as a sequence of edges.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source` until `target` is found, exploring each
|
|
/// branch as far as possible before backtracking. Returns some 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::dfs_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!(dfs_find_path(&graph, source, target), Some(vec![e]));
|
|
/// ```
|
|
///
|
|
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
|
pub fn dfs_find_path<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<Vec<G::Edge>>
|
|
where
|
|
G: GraphTopology,
|
|
{
|
|
dfs_find_path_where(graph, source, |v| v == target)
|
|
}
|
|
|
|
/// [Depth-first search] from `source` for a vertex matching `predicate`, returns the path as a
|
|
/// sequence of edges.
|
|
///
|
|
/// Traverses vertices in `graph` starting from `source` until a vertex satisfying `predicate` is
|
|
/// found, exploring each branch as far as possible before backtracking. Returns some 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::dfs_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!(dfs_find_path_where(&graph, source, |v| v == target), Some(vec![e]));
|
|
/// ```
|
|
///
|
|
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
|
pub fn dfs_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;
|
|
|
|
struct Frame<G: GraphTopology> {
|
|
arrival_edge: Option<G::Edge>,
|
|
cursor: G::IncidenceCursor,
|
|
}
|
|
|
|
let mut stack: Vec<Frame<G>> = vec![Frame {
|
|
arrival_edge: None,
|
|
cursor: graph.incidence_cursor(source),
|
|
}];
|
|
|
|
while let Some(frame) = stack.last_mut() {
|
|
match frame.cursor.next(graph) {
|
|
None => {
|
|
stack.pop();
|
|
}
|
|
Some((neighbor, edge)) => {
|
|
if predicate(neighbor) {
|
|
let mut path: Vec<G::Edge> =
|
|
stack.iter().filter_map(|f| f.arrival_edge).collect();
|
|
path.push(edge);
|
|
return Some(path);
|
|
}
|
|
if !visited[neighbor] {
|
|
visited[neighbor] = true;
|
|
stack.push(Frame {
|
|
arrival_edge: Some(edge),
|
|
cursor: graph.incidence_cursor(neighbor),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|