rust_igraph/algorithms/traversal/
dfs.rs1use std::collections::VecDeque;
11
12use crate::core::{Graph, IgraphResult, VertexId};
13
14pub fn dfs(graph: &Graph, root: VertexId) -> IgraphResult<Vec<VertexId>> {
39 let _ = graph.neighbors(root)?;
41
42 let n = graph.vcount();
43 let mut visited = vec![false; n as usize];
44 let mut order: Vec<VertexId> = Vec::with_capacity(n as usize);
45
46 let mut stack: VecDeque<(VertexId, usize, Vec<VertexId>)> = VecDeque::new();
50
51 visited[root as usize] = true;
52 order.push(root);
53 let mut root_neis = graph.neighbors(root)?;
59 root_neis.reverse();
60 stack.push_back((root, 0, root_neis));
61
62 while let Some(&(actvect, cursor, ref neis)) = stack.back() {
63 let mut next_cursor = cursor;
66 let mut found: Option<VertexId> = None;
67 while next_cursor < neis.len() {
68 let nei = neis[next_cursor];
69 next_cursor += 1;
70 if !visited[nei as usize] {
71 found = Some(nei);
72 break;
73 }
74 }
75
76 if let Some(nei) = found {
77 let last = stack.len() - 1;
79 stack[last].1 = next_cursor;
80 visited[nei as usize] = true;
81 order.push(nei);
82 let mut nei_neis = graph.neighbors(nei)?;
83 nei_neis.reverse();
84 stack.push_back((nei, 0, nei_neis));
85 } else {
86 let _ = actvect;
88 stack.pop_back();
89 }
90 }
91
92 Ok(order)
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 fn path_graph(n: u32) -> Graph {
100 let mut g = Graph::with_vertices(n);
101 for i in 0..n.saturating_sub(1) {
102 g.add_edge(i, i + 1).unwrap();
103 }
104 g
105 }
106
107 #[test]
108 fn empty_graph_errors_on_any_root() {
109 let g = Graph::with_vertices(0);
110 let err = dfs(&g, 0).unwrap_err();
111 assert!(matches!(
112 err,
113 crate::core::IgraphError::VertexOutOfRange { id: 0, n: 0 }
114 ));
115 }
116
117 #[test]
118 fn single_vertex_visits_self() {
119 let g = Graph::with_vertices(1);
120 assert_eq!(dfs(&g, 0).unwrap(), vec![0]);
121 }
122
123 #[test]
124 fn path_visits_in_order() {
125 let g = path_graph(5);
126 assert_eq!(dfs(&g, 0).unwrap(), vec![0, 1, 2, 3, 4]);
128 }
129
130 #[test]
131 fn dfs_goes_deep_before_wide() {
132 let mut g = Graph::with_vertices(4);
139 g.add_edge(0, 1).unwrap();
140 g.add_edge(0, 2).unwrap();
141 g.add_edge(1, 3).unwrap();
142 let order = dfs(&g, 0).unwrap();
143 assert_eq!(order[0], 0);
144 assert_eq!(order.len(), 4);
145 let pos = |v: u32| order.iter().position(|&x| x == v).unwrap();
146 let p1 = pos(1);
151 let p3 = pos(3);
152 assert!(p3 == p1 + 1, "3 should follow 1 directly in DFS pre-order");
153 }
154
155 #[test]
156 fn unreachable_vertex_excluded() {
157 let mut g = Graph::with_vertices(3);
158 g.add_edge(0, 1).unwrap();
159 let order = dfs(&g, 0).unwrap();
161 assert_eq!(order.len(), 2);
162 assert!(order.contains(&0));
163 assert!(order.contains(&1));
164 }
165
166 #[test]
167 fn invalid_root_errors() {
168 let g = Graph::with_vertices(2);
169 let err = dfs(&g, 5).unwrap_err();
170 match err {
171 crate::core::IgraphError::VertexOutOfRange { id, n } => {
172 assert_eq!(id, 5);
173 assert_eq!(n, 2);
174 }
175 other => panic!("unexpected error: {other:?}"),
176 }
177 }
178
179 #[test]
180 fn complete_k4_visits_all_four() {
181 let mut g = Graph::with_vertices(4);
182 for u in 0..4u32 {
183 for v in (u + 1)..4 {
184 g.add_edge(u, v).unwrap();
185 }
186 }
187 let order = dfs(&g, 0).unwrap();
188 let mut sorted = order.clone();
189 sorted.sort_unstable();
190 assert_eq!(sorted, vec![0, 1, 2, 3]);
191 assert_eq!(order[0], 0);
192 }
193}