rs_graph/search/
bfs.rs

1/*
2 * Copyright (c) 2017, 2018, 2020, 2021 Frank Fischer <frank-fischer@shadow-soft.de>
3 *
4 * This program is free software: you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation, either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program.  If not, see  <http://www.gnu.org/licenses/>
16 */
17
18//! Breadth-first-search.
19//!
20//! # Example
21//!
22//! ```
23//! use rs_graph::LinkedListGraph;
24//! use rs_graph::traits::*;
25//! use rs_graph::classes;
26//! use rs_graph::search::bfs;
27//!
28//! let g: LinkedListGraph = classes::peterson();
29//! let mut cnt = 0;
30//! for (u, e) in bfs::start(g.neighbors(), g.id2node(0)) {
31//!     assert_ne!(g.node_id(u), 0);
32//!     cnt += 1;
33//! }
34//! assert_eq!(cnt, g.num_nodes() - 1);
35//! ```
36
37use crate::adjacencies::Adjacencies;
38use crate::collections::{ItemMap, ItemQueue};
39use crate::traits::GraphIterator;
40use std::collections::{HashMap, VecDeque};
41use std::hash::Hash;
42
43/// BFS iterator with default data structures.
44pub type BFSDefault<'a, A> = BFS<
45    'a,
46    A,
47    HashMap<<A as Adjacencies<'a>>::Node, <A as Adjacencies<'a>>::Edge>,
48    VecDeque<<A as Adjacencies<'a>>::Node>,
49>;
50
51/// The default data structures for BFS.
52pub type DefaultData<N, E> = (HashMap<N, E>, VecDeque<N>);
53
54/// Start and return a BFS iterator using default data structures.
55///
56/// This is a convenience wrapper around [`start_with_data`] using the default
57/// data structures [`DefaultData`].
58///
59/// # Parameter
60/// - `adj`: adjacency information for the graph
61/// - `src`: the source node at which the search should start.
62pub fn start<'a, A>(adj: A, src: A::Node) -> BFSDefault<'a, A>
63where
64    A: Adjacencies<'a>,
65    A::Node: Hash,
66{
67    start_with_data(adj, src, DefaultData::default())
68}
69
70/// Start and return a BFS iterator with user defined data structures.
71///
72/// The returned iterator traverses the edges in breadth-first order. The
73/// iterator returns the next node and its incoming edge.
74///
75/// Note that the start node is *not* returned by the iterator.
76///
77/// The algorithm requires a pair `(M, Q)` with `M` implementing [`ItemMap<Node,
78/// Edge>`][crate::collections::ItemMap], and `Q` implementing
79/// [`ItemQueue<Node>`][crate::collections::ItemQueue] as internal data
80/// structures. The map is used to store the last edge of the path from the
81/// source to each reachable node. The queue is used to handle the nodes in
82/// breadth-first order. The data structures can be reused for multiple
83/// searches.
84///
85/// # Parameter
86/// - `adj`: adjacency information for the graph
87/// - `src`: the source node at which the search should start.
88/// - `data`: the data structures used in the algorithm
89///
90/// # Example
91///
92/// ```
93/// use rs_graph::LinkedListGraph;
94/// use rs_graph::traits::*;
95/// use rs_graph::classes;
96/// use rs_graph::search::bfs;
97/// use std::collections::{HashMap, VecDeque};
98///
99/// let g: LinkedListGraph = classes::peterson();
100/// let mut cnt = 0;
101/// for (u, e) in bfs::start_with_data(g.neighbors(), g.id2node(0),
102///                                    (HashMap::new(), VecDeque::new()))
103/// {
104///     assert_ne!(g.node_id(u), 0);
105///     cnt += 1;
106/// }
107/// assert_eq!(cnt, g.num_nodes() - 1);
108/// ```
109pub fn start_with_data<'a, A, S, Q>(adj: A, src: A::Node, data: (S, Q)) -> BFS<'a, A, S, Q>
110where
111    A: Adjacencies<'a>,
112    S: ItemMap<A::Node, A::Edge>,
113    Q: ItemQueue<A::Node>,
114{
115    let (mut seen, mut queue) = data;
116    queue.clear();
117    seen.clear();
118    let it = adj.neigh_iter(src);
119
120    BFS {
121        adj,
122        src,
123        seen,
124        queue,
125        it,
126    }
127}
128
129/// The BFS iterator.
130pub struct BFS<'a, A, S, Q>
131where
132    A: Adjacencies<'a>,
133    S: ItemMap<A::Node, A::Edge>,
134    Q: ItemQueue<A::Node>,
135{
136    adj: A,
137    src: A::Node,
138    seen: S,
139    queue: Q,
140    it: A::IncidenceIt,
141}
142
143impl<'a, A, S, Q> Iterator for BFS<'a, A, S, Q>
144where
145    A: Adjacencies<'a>,
146    S: ItemMap<A::Node, A::Edge>,
147    Q: ItemQueue<A::Node>,
148{
149    type Item = (A::Node, A::Edge);
150
151    fn next(&mut self) -> Option<Self::Item> {
152        loop {
153            while let Some((e, v)) = self.it.next(&self.adj) {
154                if v != self.src && self.seen.insert(v, e) {
155                    self.queue.push(v);
156                    return Some((v, e));
157                }
158            }
159            if let Some(u) = self.queue.pop() {
160                self.it = self.adj.neigh_iter(u);
161            } else {
162                return None;
163            }
164        }
165    }
166}
167
168impl<'a, A, S, Q> BFS<'a, A, S, Q>
169where
170    A: Adjacencies<'a>,
171    S: ItemMap<A::Node, A::Edge>,
172    Q: ItemQueue<A::Node>,
173{
174    /// Run the bfs completely.
175    ///
176    /// Note that this method may run forever on an infinite graph.
177    pub fn run(&mut self) {
178        while self.next().is_some() {}
179    }
180
181    /// Return the data structures used in the search.
182    pub fn into_data(self) -> (S, Q) {
183        (self.seen, self.queue)
184    }
185
186    /// Return the incoming edge of a node.
187    pub fn incoming_edge(&self, u: A::Node) -> Option<A::Edge> {
188        self.seen.get(u).cloned()
189    }
190}