graaf/algo/tarjan.rs
1//! Tarjan's algorithm.
2//!
3//! Tarjan's algorithm finds a digraph's strongly connected components.
4//!
5//! # Examples
6//!
7//! There are three strongly connected components in this digraph:
8//!
9//! 
10//!
11//! ```
12//! use {
13//! graaf::{
14//! AddArc,
15//! AdjacencyList,
16//! Empty,
17//! Tarjan,
18//! },
19//! std::collections::BTreeSet,
20//! };
21//!
22//! let mut digraph = AdjacencyList::empty(8);
23//!
24//! digraph.add_arc(0, 1);
25//! digraph.add_arc(1, 2);
26//! digraph.add_arc(1, 4);
27//! digraph.add_arc(2, 3);
28//! digraph.add_arc(2, 6);
29//! digraph.add_arc(3, 2);
30//! digraph.add_arc(3, 7);
31//! digraph.add_arc(4, 0);
32//! digraph.add_arc(4, 5);
33//! digraph.add_arc(5, 6);
34//! digraph.add_arc(6, 5);
35//! digraph.add_arc(7, 3);
36//! digraph.add_arc(7, 6);
37//!
38//! assert!(Tarjan::new(&digraph).components().iter().eq(&[
39//! BTreeSet::from([5, 6]),
40//! BTreeSet::from([7, 3, 2]),
41//! BTreeSet::from([4, 1, 0])
42//! ]));
43//! ```
44
45use {
46 crate::{
47 OutNeighbors,
48 Vertices,
49 },
50 std::collections::{
51 BTreeMap,
52 BTreeSet,
53 },
54};
55
56/// Tarjan's algorithm.
57///
58/// Tarjan's algorithm finds a digraph's strongly connected components.
59///
60/// # Examples
61///
62/// There are three strongly connected components in this digraph:
63///
64/// 
65///
66/// ```
67/// use {
68/// graaf::{
69/// AddArc,
70/// AdjacencyList,
71/// Empty,
72/// Tarjan,
73/// },
74/// std::collections::BTreeSet,
75/// };
76///
77/// let mut digraph = AdjacencyList::empty(8);
78///
79/// digraph.add_arc(0, 1);
80/// digraph.add_arc(1, 2);
81/// digraph.add_arc(1, 4);
82/// digraph.add_arc(2, 3);
83/// digraph.add_arc(2, 6);
84/// digraph.add_arc(3, 2);
85/// digraph.add_arc(3, 7);
86/// digraph.add_arc(4, 0);
87/// digraph.add_arc(4, 5);
88/// digraph.add_arc(5, 6);
89/// digraph.add_arc(6, 5);
90/// digraph.add_arc(7, 3);
91/// digraph.add_arc(7, 6);
92///
93/// assert!(Tarjan::new(&digraph).components().iter().eq(&[
94/// BTreeSet::from([5, 6]),
95/// BTreeSet::from([7, 3, 2]),
96/// BTreeSet::from([4, 1, 0]),
97/// ]));
98/// ```
99#[derive(Clone, Debug, Eq, PartialEq)]
100pub struct Tarjan<'a, D> {
101 digraph: &'a D,
102 i: usize,
103 stack: Vec<usize>,
104 on_stack: BTreeSet<usize>,
105 index: BTreeMap<usize, usize>,
106 low_link: BTreeMap<usize, usize>,
107 components: Vec<BTreeSet<usize>>,
108}
109
110impl<'a, D> Tarjan<'a, D> {
111 /// Construct a new instance of Tarjan's algorithm.
112 ///
113 /// # Arguments
114 ///
115 /// * `digraph`: The digraph.
116 #[must_use]
117 pub const fn new(digraph: &'a D) -> Self
118 where
119 D: OutNeighbors + Vertices,
120 {
121 Self {
122 digraph,
123 i: 0,
124 stack: Vec::new(),
125 on_stack: BTreeSet::new(),
126 index: BTreeMap::new(),
127 low_link: BTreeMap::new(),
128 components: Vec::new(),
129 }
130 }
131
132 /// Find a digraph's strongly connected components.
133 ///
134 /// # Examples
135 ///
136 /// There are three strongly connected components in this digraph:
137 ///
138 /// 
139 ///
140 /// ```
141 /// use {
142 /// graaf::{
143 /// AddArc,
144 /// AdjacencyList,
145 /// Empty,
146 /// Tarjan,
147 /// },
148 /// std::collections::BTreeSet,
149 /// };
150 ///
151 /// let mut digraph = AdjacencyList::empty(8);
152 ///
153 /// digraph.add_arc(0, 1);
154 /// digraph.add_arc(1, 2);
155 /// digraph.add_arc(1, 4);
156 /// digraph.add_arc(2, 3);
157 /// digraph.add_arc(2, 6);
158 /// digraph.add_arc(3, 2);
159 /// digraph.add_arc(3, 7);
160 /// digraph.add_arc(4, 0);
161 /// digraph.add_arc(4, 5);
162 /// digraph.add_arc(5, 6);
163 /// digraph.add_arc(6, 5);
164 /// digraph.add_arc(7, 3);
165 /// digraph.add_arc(7, 6);
166 ///
167 /// assert!(Tarjan::new(&digraph).components().iter().eq(&[
168 /// BTreeSet::from([5, 6]),
169 /// BTreeSet::from([7, 3, 2]),
170 /// BTreeSet::from([4, 1, 0])
171 /// ]));
172 /// ```
173 #[must_use]
174 pub fn components(&mut self) -> &Vec<BTreeSet<usize>>
175 where
176 D: OutNeighbors + Vertices,
177 {
178 for u in self.digraph.vertices() {
179 if !self.index.contains_key(&u) {
180 self.connect(u);
181 }
182 }
183
184 &self.components
185 }
186
187 fn connect(&mut self, u: usize)
188 where
189 D: OutNeighbors,
190 {
191 let _ = self.index.insert(u, self.i);
192 let _ = self.low_link.insert(u, self.i);
193 let _ = self.on_stack.insert(u);
194
195 self.stack.push(u);
196
197 self.i += 1;
198
199 for v in self.digraph.out_neighbors(u) {
200 if let Some(&w) = self.index.get(&v) {
201 if self.on_stack.contains(&v) {
202 let _ = self.low_link.insert(u, self.low_link[&u].min(w));
203 }
204 } else {
205 self.connect(v);
206
207 let _ = self
208 .low_link
209 .insert(u, self.low_link[&u].min(self.low_link[&v]));
210 }
211 }
212
213 if self.index.get(&u) == self.low_link.get(&u) {
214 let mut component = BTreeSet::new();
215
216 while let Some(v) = self.stack.pop() {
217 let _ = self.on_stack.remove(&v);
218 let _ = component.insert(v);
219
220 if u == v {
221 break;
222 }
223 }
224
225 self.components.push(component);
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use {
233 super::*,
234 crate::{
235 AdjacencyList,
236 Empty,
237 repr::adjacency_list::fixture::{
238 bang_jensen_34,
239 bang_jensen_94,
240 bang_jensen_196,
241 kattis_builddeps,
242 kattis_cantinaofbabel_1,
243 kattis_cantinaofbabel_2,
244 kattis_escapewallmaria_1,
245 kattis_escapewallmaria_2,
246 kattis_escapewallmaria_3,
247 },
248 },
249 };
250
251 #[test]
252 fn components_bang_jensen_196() {
253 assert!(Tarjan::new(&bang_jensen_196()).components().iter().eq(&[
254 BTreeSet::from([2, 3, 4]),
255 BTreeSet::from([5, 6, 7]),
256 BTreeSet::from([0, 1]),
257 ]));
258 }
259
260 #[test]
261 fn components_bang_jensen_34() {
262 assert!(Tarjan::new(&bang_jensen_34()).components().iter().eq(&[
263 BTreeSet::from([4]),
264 BTreeSet::from([0]),
265 BTreeSet::from([1]),
266 BTreeSet::from([3]),
267 BTreeSet::from([5]),
268 BTreeSet::from([2]),
269 ]));
270 }
271
272 #[test]
273 fn components_bang_jensen_94() {
274 assert!(Tarjan::new(&bang_jensen_94()).components().iter().eq(&[
275 BTreeSet::from([5]),
276 BTreeSet::from([3]),
277 BTreeSet::from([1]),
278 BTreeSet::from([6]),
279 BTreeSet::from([4]),
280 BTreeSet::from([2]),
281 BTreeSet::from([0]),
282 ]));
283 }
284
285 #[test]
286 fn components_kattis_builddeps() {
287 assert!(Tarjan::new(&kattis_builddeps()).components().iter().eq(&[
288 BTreeSet::from([1]),
289 BTreeSet::from([3]),
290 BTreeSet::from([4]),
291 BTreeSet::from([0]),
292 BTreeSet::from([5]),
293 BTreeSet::from([2]),
294 ]));
295 }
296
297 #[test]
298 fn components_kattis_cantinaofbabel_1() {
299 assert!(
300 Tarjan::new(&kattis_cantinaofbabel_1())
301 .components()
302 .iter()
303 .eq(&[
304 BTreeSet::from([5, 6, 10]),
305 BTreeSet::from([0, 1, 2, 3, 4, 7, 9, 11]),
306 BTreeSet::from([8]),
307 ])
308 );
309 }
310
311 #[test]
312 fn components_kattis_cantinaofbabel_2() {
313 assert!(
314 Tarjan::new(&kattis_cantinaofbabel_2())
315 .components()
316 .iter()
317 .eq(&[
318 BTreeSet::from([3, 4]),
319 BTreeSet::from([5, 6]),
320 BTreeSet::from([0, 1, 2, 7]),
321 BTreeSet::from([8, 9, 10, 11]),
322 ])
323 );
324 }
325
326 #[test]
327 fn components_kattis_escapewallmaria_1() {
328 assert!(
329 Tarjan::new(&kattis_escapewallmaria_1())
330 .components()
331 .iter()
332 .eq(&[
333 BTreeSet::from([0]),
334 BTreeSet::from([1]),
335 BTreeSet::from([2]),
336 BTreeSet::from([3]),
337 BTreeSet::from([4]),
338 BTreeSet::from([12]),
339 BTreeSet::from([5, 6, 9, 13]),
340 BTreeSet::from([7]),
341 BTreeSet::from([8]),
342 BTreeSet::from([10]),
343 BTreeSet::from([11]),
344 BTreeSet::from([14]),
345 BTreeSet::from([15]),
346 ])
347 );
348 }
349
350 #[test]
351 fn components_kattis_escapewallmaria_2() {
352 assert!(
353 Tarjan::new(&kattis_escapewallmaria_2())
354 .components()
355 .iter()
356 .eq(&[
357 BTreeSet::from([0]),
358 BTreeSet::from([1]),
359 BTreeSet::from([2]),
360 BTreeSet::from([3]),
361 BTreeSet::from([4]),
362 BTreeSet::from([5, 6, 9]),
363 BTreeSet::from([7]),
364 BTreeSet::from([8]),
365 BTreeSet::from([10]),
366 BTreeSet::from([11]),
367 BTreeSet::from([12, 13]),
368 BTreeSet::from([14]),
369 BTreeSet::from([15]),
370 ])
371 );
372 }
373
374 #[test]
375 fn components_kattis_escapewallmaria_3() {
376 assert!(
377 Tarjan::new(&kattis_escapewallmaria_3())
378 .components()
379 .iter()
380 .eq(&[
381 BTreeSet::from([0]),
382 BTreeSet::from([1, 2, 5, 6, 9, 12, 13]),
383 BTreeSet::from([3]),
384 BTreeSet::from([4]),
385 BTreeSet::from([7]),
386 BTreeSet::from([8]),
387 BTreeSet::from([10]),
388 BTreeSet::from([11]),
389 BTreeSet::from([14]),
390 BTreeSet::from([15]),
391 ])
392 );
393 }
394
395 #[test]
396 fn components_trivial() {
397 assert!(
398 Tarjan::new(&AdjacencyList::trivial())
399 .components()
400 .iter()
401 .eq(&[BTreeSet::from([0])])
402 );
403 }
404}