//! 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 { /// Vertex map of minimum distances from a given `source` vertex. pub distances: EntityMap>, /// Vertex map of predecessors on some shortest path from a given `source` vertex. pub predecessors: EntityMap>, } // 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(graph: &G, source: G::Vertex, weights: W) -> DijkstraResult 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( graph: &G, source: G::Vertex, weights: W, ) -> EntityMap> 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(graph: &G, source: G::Vertex) -> DijkstraResult 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( graph: &G, source: G::Vertex, ) -> EntityMap> where G: GraphTopology, G::Vertex: Hash, { dijkstra_distances(graph, source, |_| 1) } fn dijkstra_impl( graph: &G, source: G::Vertex, weights: W, mut on_relax: F, ) -> EntityMap> 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 { /// Vertex map of minimum distances from a given `source` vertex. pub distances: EntityMap>, /// Vertex map of predecessors on some shortest path from a given `source` vertex. pub predecessors: EntityMap>, } /// [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(graph: &G, source: G::Vertex) -> BfsResult 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(graph: &G, source: G::Vertex) -> EntityMap> 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(graph: &G, source: G::Vertex, target: G::Vertex) -> Option 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(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 { distances: EntityMap>, found: Option<(V, u32)>, } fn bfs_impl(graph: &G, source: G::Vertex, mut on_discover: F) -> BfsImplResult 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 { /// Vertex map indicating which vertices were visited during the search. pub visited: EntityMap, /// Vertex map of predecessors on the DFS tree path from a given `source` vertex. pub predecessors: EntityMap>, } /// [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(graph: &G, source: G::Vertex) -> DfsResult 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(graph: &G, source: G::Vertex) -> EntityMap 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(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(graph: &G, source: G::Vertex, predicate: P) -> Option where G: GraphTopology, P: Fn(G::Vertex) -> bool, { if predicate(source) { return Some(source); } dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found } struct DfsImplResult { visited: EntityMap, found: Option, } fn dfs_impl(graph: &G, source: G::Vertex, mut on_discover: F) -> DfsImplResult 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::)]; 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(graph: &G, source: G::Vertex, target: G::Vertex) -> Option> 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(graph: &G, source: G::Vertex, predicate: P) -> Option> 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 { arrival_edge: Option, cursor: G::IncidenceCursor, } let mut stack: Vec> = 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 = 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 }