Skip to content

Commit ca7da87

Browse files
committed
Split file "traversal.rs" into "bfs.rs" and "dfs.rs"
1 parent dd42801 commit ca7da87

File tree

4 files changed

+404
-400
lines changed

4 files changed

+404
-400
lines changed

crates/algos/src/bfs.rs

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
use std::{collections::VecDeque, hash::Hash};
2+
3+
use bitvec::prelude::*;
4+
5+
use crate::prelude::*;
6+
7+
pub fn bfs_directed<NI, G>(
8+
graph: &G,
9+
node_id: NI,
10+
direction: Direction,
11+
) -> DirectedBreadthFirst<'_, G, NI>
12+
where
13+
NI: Idx + Hash,
14+
G: Graph<NI> + DirectedDegrees<NI> + DirectedNeighbors<NI> + Sync,
15+
{
16+
DirectedBreadthFirst::new(graph, node_id, direction)
17+
}
18+
19+
pub struct DirectedBreadthFirst<'a, G, NI> {
20+
graph: &'a G,
21+
visited: BitVec<usize>,
22+
queue: VecDeque<NI>,
23+
direction: Direction,
24+
}
25+
26+
impl<'a, G, NI> DirectedBreadthFirst<'a, G, NI>
27+
where
28+
NI: Idx + Hash,
29+
G: Graph<NI> + DirectedNeighbors<NI> + Sync,
30+
{
31+
pub fn new(graph: &'a G, node_id: NI, direction: Direction) -> Self {
32+
Self {
33+
graph,
34+
visited: BitVec::repeat(false, graph.node_count().index()),
35+
queue: VecDeque::from_iter([node_id]),
36+
direction,
37+
}
38+
}
39+
40+
fn dequeue(&mut self) -> Option<NI> {
41+
loop {
42+
let node_id = self.queue.pop_front()?;
43+
44+
if !self.visited.replace(node_id.index(), true) {
45+
return Some(node_id);
46+
}
47+
}
48+
}
49+
50+
fn enqueue_out_neighbors(&mut self, node_id: NI) {
51+
let neighbors = self
52+
.graph
53+
.out_neighbors(node_id)
54+
.filter(|&node_id| !self.visited[node_id.index()]);
55+
self.queue.extend(neighbors);
56+
}
57+
58+
fn enqueue_in_neighbors(&mut self, node_id: NI) {
59+
let neighbors = self
60+
.graph
61+
.in_neighbors(node_id)
62+
.filter(|&node_id| !self.visited[node_id.index()]);
63+
self.queue.extend(neighbors);
64+
}
65+
}
66+
67+
impl<'a, G, NI> Iterator for DirectedBreadthFirst<'a, G, NI>
68+
where
69+
NI: Idx + Hash,
70+
G: Graph<NI> + DirectedNeighbors<NI> + Sync,
71+
{
72+
type Item = NI;
73+
74+
fn next(&mut self) -> Option<Self::Item> {
75+
let node_id = self.dequeue()?;
76+
77+
match self.direction {
78+
Direction::Outgoing => self.enqueue_out_neighbors(node_id),
79+
Direction::Incoming => self.enqueue_in_neighbors(node_id),
80+
Direction::Undirected => {
81+
self.enqueue_out_neighbors(node_id);
82+
self.enqueue_in_neighbors(node_id);
83+
}
84+
}
85+
86+
Some(node_id)
87+
}
88+
}
89+
90+
pub fn bfs_undirected<NI, G>(graph: &G, node_id: NI) -> UndirectedBreadthFirst<'_, G, NI>
91+
where
92+
NI: Idx + Hash,
93+
G: Graph<NI> + UndirectedDegrees<NI> + UndirectedNeighbors<NI> + Sync,
94+
{
95+
UndirectedBreadthFirst::new(graph, node_id)
96+
}
97+
98+
pub struct UndirectedBreadthFirst<'a, G, NI> {
99+
graph: &'a G,
100+
visited: BitVec<usize>,
101+
queue: VecDeque<NI>,
102+
}
103+
104+
impl<'a, G, NI> UndirectedBreadthFirst<'a, G, NI>
105+
where
106+
NI: Idx + Hash + std::fmt::Debug,
107+
G: Graph<NI> + UndirectedNeighbors<NI> + Sync,
108+
{
109+
pub fn new(graph: &'a G, node_id: NI) -> Self {
110+
Self {
111+
graph,
112+
visited: BitVec::repeat(false, graph.node_count().index()),
113+
queue: VecDeque::from_iter([node_id]),
114+
}
115+
}
116+
117+
fn dequeue(&mut self) -> Option<NI> {
118+
loop {
119+
let node_id = self.queue.pop_front()?;
120+
121+
if !self.visited.replace(node_id.index(), true) {
122+
return Some(node_id);
123+
}
124+
}
125+
}
126+
127+
fn enqueue_neighbors(&mut self, node_id: NI) {
128+
let neighbors = self
129+
.graph
130+
.neighbors(node_id)
131+
.filter(|&node_id| !self.visited[node_id.index()]);
132+
133+
let neighbors: Vec<_> = neighbors.collect();
134+
135+
self.queue.extend(neighbors);
136+
}
137+
}
138+
139+
impl<'a, G, NI> Iterator for UndirectedBreadthFirst<'a, G, NI>
140+
where
141+
NI: Idx + Hash,
142+
G: Graph<NI> + UndirectedNeighbors<NI> + Sync,
143+
{
144+
type Item = NI;
145+
146+
fn next(&mut self) -> Option<Self::Item> {
147+
let node_id = self.dequeue()?;
148+
149+
self.enqueue_neighbors(node_id);
150+
151+
Some(node_id)
152+
}
153+
}
154+
155+
#[cfg(test)]
156+
mod tests {
157+
use super::*;
158+
use crate::prelude::{CsrLayout, GraphBuilder};
159+
160+
use super::*;
161+
mod directed {
162+
use super::*;
163+
164+
#[test]
165+
fn acyclic() {
166+
let graph: DirectedCsrGraph<usize> = GraphBuilder::new()
167+
.csr_layout(CsrLayout::Deduplicated)
168+
.edges(vec![(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (2, 1), (3, 1)])
169+
.build();
170+
171+
let actual: Vec<usize> = bfs_directed(&graph, 0, Direction::Outgoing).collect();
172+
let expected: Vec<usize> = vec![0, 1, 2, 3];
173+
174+
assert_eq!(actual, expected);
175+
}
176+
177+
#[test]
178+
fn cyclic() {
179+
let graph: DirectedCsrGraph<usize> = GraphBuilder::new()
180+
.csr_layout(CsrLayout::Deduplicated)
181+
.edges(vec![(0, 1), (0, 2), (1, 2), (1, 3), (2, 1), (2, 1), (3, 1)])
182+
.build();
183+
184+
let actual: Vec<usize> = bfs_directed(&graph, 0, Direction::Outgoing).collect();
185+
let expected: Vec<usize> = vec![0, 1, 2, 3];
186+
187+
assert_eq!(actual, expected);
188+
}
189+
}
190+
191+
#[test]
192+
fn undirected() {
193+
let graph: UndirectedCsrGraph<usize> = GraphBuilder::new()
194+
.csr_layout(CsrLayout::Deduplicated)
195+
.edges(vec![(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (2, 1), (3, 1)])
196+
.build();
197+
198+
let actual: Vec<usize> = bfs_undirected(&graph, 0).collect();
199+
let expected: Vec<usize> = vec![0, 1, 2, 3];
200+
201+
assert_eq!(actual, expected);
202+
}
203+
}

0 commit comments

Comments
 (0)