graaf/algo/
bfs.rs

1//! Breadth-first search.
2//!
3//! Breadth-first search explores an unweighted digraph's vertices in the order
4//! of their distance from a source.
5//!
6//! The time complexity is `O(v + a)`, where `v` is the digraph's order and
7//! `a` is the digraph's size.
8//!
9//! # Examples
10//!
11//! ## Single source
12//!
13//! The path from vertex `0` is red. `t` denotes the iteration indices.
14//!
15//! ![A digraph and the breadth-first search from vertex `0`](https://raw.githubusercontent.com/bsdrks/graaf-images/main/out/bfs_1-0.87.4.svg?)
16//!
17//! ```
18//! use {
19//!     graaf::{
20//!         AddArc,
21//!         AdjacencyList,
22//!         Bfs,
23//!         Empty,
24//!     },
25//!     std::iter::once,
26//! };
27//!
28//! let mut digraph = AdjacencyList::empty(6);
29//!
30//! digraph.add_arc(0, 1);
31//! digraph.add_arc(1, 2);
32//! digraph.add_arc(1, 4);
33//! digraph.add_arc(2, 5);
34//! digraph.add_arc(3, 0);
35//!
36//! assert!(Bfs::new(&digraph, once(0)).eq([0, 1, 2, 4, 5]));
37//! ```
38//!
39//! ## Multiple sources
40//!
41//! The path from vertex `0` is red. The path from vertex `1` is blue.
42//!
43//! ![A digraph and a breadth-first traversal from two source vertices](https://raw.githubusercontent.com/bsdrks/graaf-images/main/out/bfs_multi_source_1-0.87.4.svg?)
44//!
45//! ```
46//! use graaf::{
47//!     AddArc,
48//!     AdjacencyList,
49//!     Bfs,
50//!     Empty,
51//! };
52//!
53//! let mut digraph = AdjacencyList::empty(8);
54//!
55//! digraph.add_arc(0, 1);
56//! digraph.add_arc(1, 2);
57//! digraph.add_arc(1, 4);
58//! digraph.add_arc(2, 3);
59//! digraph.add_arc(2, 5);
60//! digraph.add_arc(2, 6);
61//! digraph.add_arc(3, 0);
62//! digraph.add_arc(6, 5);
63//! digraph.add_arc(6, 7);
64//! digraph.add_arc(7, 6);
65//!
66//! assert!(
67//!     Bfs::new(&digraph, [3, 7].into_iter()).eq([3, 7, 0, 6, 1, 5, 2, 4])
68//! );
69//! ```
70
71use {
72    crate::{
73        Order,
74        OutNeighbors,
75    },
76    std::collections::VecDeque,
77};
78
79/// Breadth-first search.
80///
81/// Breadth-first search explores an unweighted digraph's vertices in the order
82/// of their distance from a source.
83///
84/// # Examples
85///
86/// The path from vertex `0` is red. `t` denotes the iteration indices.
87///
88/// ![A digraph and the breadth-first search from vertex `0`](https://raw.githubusercontent.com/bsdrks/graaf-images/main/out/bfs_1-0.87.4.svg?)
89///
90/// ```
91/// use {
92///     graaf::{
93///         AddArc,
94///         AdjacencyList,
95///         Bfs,
96///         Empty,
97///     },
98///     std::iter::once,
99/// };
100///
101/// let mut digraph = AdjacencyList::empty(6);
102///
103/// digraph.add_arc(0, 1);
104/// digraph.add_arc(1, 2);
105/// digraph.add_arc(1, 4);
106/// digraph.add_arc(2, 5);
107/// digraph.add_arc(3, 0);
108///
109/// assert!(Bfs::new(&digraph, once(0)).eq([0, 1, 2, 4, 5]));
110/// ```
111///
112/// ## Multiple sources
113///
114/// The path from vertex `0` is red. The path from vertex `1` is blue.
115///
116/// ![A digraph and a breadth-first traversal from two source vertices](https://raw.githubusercontent.com/bsdrks/graaf-images/main/out/bfs_multi_source_1-0.87.4.svg?)
117///
118/// ```
119/// use graaf::{
120///     AddArc,
121///     AdjacencyList,
122///     Bfs,
123///     Empty,
124/// };
125///
126/// let mut digraph = AdjacencyList::empty(8);
127///
128/// digraph.add_arc(0, 1);
129/// digraph.add_arc(1, 2);
130/// digraph.add_arc(1, 4);
131/// digraph.add_arc(2, 3);
132/// digraph.add_arc(2, 5);
133/// digraph.add_arc(2, 6);
134/// digraph.add_arc(3, 0);
135/// digraph.add_arc(6, 5);
136/// digraph.add_arc(6, 7);
137/// digraph.add_arc(7, 6);
138///
139/// assert!(
140///     Bfs::new(&digraph, [3, 7].into_iter()).eq([3, 7, 0, 6, 1, 5, 2, 4])
141/// );
142/// ```
143#[derive(Clone, Debug, Eq, PartialEq)]
144pub struct Bfs<'a, D> {
145    digraph: &'a D,
146    queue: VecDeque<usize>,
147    visited: Vec<bool>,
148}
149
150impl<'a, D> Bfs<'a, D> {
151    /// Construct a new breadth-first search.
152    ///
153    /// # Arguments
154    ///
155    /// * `digraph`: The digraph.
156    /// * `sources`: The source vertices.
157    #[must_use]
158    pub fn new<T>(digraph: &'a D, sources: T) -> Self
159    where
160        D: Order,
161        T: Iterator<Item = usize> + Clone,
162    {
163        let order = digraph.order();
164        let mut queue = VecDeque::with_capacity(order);
165        let mut visited = vec![false; order];
166        let visited_ptr = visited.as_mut_ptr();
167
168        for u in sources {
169            queue.push_back(u);
170
171            unsafe {
172                *visited_ptr.add(u) = true;
173            }
174        }
175
176        Self {
177            digraph,
178            queue,
179            visited,
180        }
181    }
182}
183
184impl<D> Iterator for Bfs<'_, D>
185where
186    D: OutNeighbors,
187{
188    type Item = usize;
189
190    fn next(&mut self) -> Option<Self::Item> {
191        let u = self.queue.pop_front()?;
192        let visited_ptr = self.visited.as_mut_ptr();
193
194        for v in self.digraph.out_neighbors(u) {
195            let visited_v = unsafe { visited_ptr.add(v) };
196
197            unsafe {
198                if !*visited_v {
199                    *visited_v = true;
200
201                    self.queue.push_back(v);
202                }
203            }
204        }
205
206        Some(u)
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use {
213        super::*,
214        crate::repr::adjacency_list::fixture::{
215            bang_jensen_34,
216            bang_jensen_94,
217            bang_jensen_196,
218            kattis_builddeps,
219            kattis_cantinaofbabel_1,
220            kattis_cantinaofbabel_2,
221            kattis_escapewallmaria_1,
222            kattis_escapewallmaria_2,
223            kattis_escapewallmaria_3,
224        },
225        std::iter::once,
226    };
227
228    #[test]
229    fn iter_bang_jensen_196() {
230        let digraph = bang_jensen_196();
231
232        assert!(Bfs::new(&digraph, once(0)).eq([0, 1, 4, 7, 2, 5, 3, 6]));
233    }
234
235    #[test]
236    fn iter_bang_jensen_34() {
237        let digraph = bang_jensen_34();
238
239        assert!(Bfs::new(&digraph, once(0)).eq([0, 4]));
240    }
241
242    #[test]
243    fn iter_bang_jensen_94() {
244        let digraph = bang_jensen_94();
245
246        assert!(Bfs::new(&digraph, once(0)).eq([0, 1, 2, 3, 4, 5, 6]));
247    }
248
249    #[test]
250    fn iter_kattis_builddeps() {
251        let digraph = kattis_builddeps();
252
253        assert!(Bfs::new(&digraph, once(0)).eq([0, 3, 4, 1]));
254    }
255
256    #[test]
257    fn iter_kattis_cantinaofbabel_1() {
258        let digraph = kattis_cantinaofbabel_1();
259
260        assert!(
261            Bfs::new(&digraph, once(0))
262                .eq([0, 1, 2, 4, 3, 5, 7, 10, 11, 6, 9])
263        );
264    }
265
266    #[test]
267    fn iter_kattis_cantinaofbabel_2() {
268        let digraph = kattis_cantinaofbabel_2();
269
270        assert!(Bfs::new(&digraph, once(0)).eq([0, 1, 7, 2, 5, 3, 6, 4]));
271    }
272
273    #[test]
274    fn iter_kattis_escapewallmaria_1() {
275        let digraph = kattis_escapewallmaria_1();
276
277        assert!(Bfs::new(&digraph, once(5)).eq([5, 6, 9, 13, 12]));
278    }
279
280    #[test]
281    fn iter_kattis_escapewallmaria_2() {
282        let digraph = kattis_escapewallmaria_2();
283
284        assert!(Bfs::new(&digraph, once(5)).eq([5, 6, 9]));
285    }
286
287    #[test]
288    fn iter_kattis_escapewallmaria_3() {
289        let digraph = kattis_escapewallmaria_3();
290
291        assert!(Bfs::new(&digraph, once(1)).eq([1, 2, 5, 6, 9, 13, 12]));
292    }
293}