Skip to content

Commit 44ecece

Browse files
authored
Add Tarjan's Strongly Connected Components algorithm (rust-lang#309)
1 parent bc94271 commit 44ecece

File tree

5 files changed

+170
-8
lines changed

5 files changed

+170
-8
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
* [Prufer Code](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/prufer_code.rs)
5555
* [Lowest Common Ancestor](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/lowest_common_ancestor.rs)
5656
* [Disjoint Set Union](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/disjoint_set_union.rs)
57+
* [Tarjan's Strongly Connected Components](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/strongly_connected_components.rs)
5758
* [Lib](https://github.com/TheAlgorithms/Rust/blob/master/src/lib.rs)
5859
* Math
5960
* [Baby-Step Giant-Step Algorithm](https://github.com/TheAlgorithms/Rust/blob/master/src/math/baby_step_giant_step.rs)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ These are for demonstration purposes only.
3737
- [x] [Bellman-Ford](./src/graph/bellman_ford.rs)
3838
- [x] [Prufer Code](./src/graph/prufer_code.rs)
3939
- [x] [Lowest Common Ancestor](./src/graph/lowest_common_ancestor.rs)
40+
- [x] [Tarjan's Strongly Connected Components](./src/graph/strongly_connected_components.rs)
4041
- [x] [Topological sorting](./src/graph/topological_sort.rs)
4142

4243
## [Math](./src/math)

src/data_structures/binary_search_tree.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,21 +101,15 @@ where
101101
pub fn minimum(&self) -> Option<&T> {
102102
match &self.left {
103103
Some(node) => node.minimum(),
104-
None => match &self.value {
105-
Some(value) => Some(value),
106-
None => None,
107-
},
104+
None => self.value.as_ref(),
108105
}
109106
}
110107

111108
/// Returns the largest value in this tree
112109
pub fn maximum(&self) -> Option<&T> {
113110
match &self.right {
114111
Some(node) => node.maximum(),
115-
None => match &self.value {
116-
Some(value) => Some(value),
117-
None => None,
118-
},
112+
None => self.value.as_ref(),
119113
}
120114
}
121115

src/graph/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod lowest_common_ancestor;
99
mod minimum_spanning_tree;
1010
mod prim;
1111
mod prufer_code;
12+
mod strongly_connected_components;
1213
mod topological_sort;
1314

1415
pub use self::bellman_ford::bellman_ford;
@@ -22,4 +23,5 @@ pub use self::lowest_common_ancestor::{LowestCommonAncestorOffline, LowestCommon
2223
pub use self::minimum_spanning_tree::kruskal;
2324
pub use self::prim::{prim, prim_with_start};
2425
pub use self::prufer_code::{prufer_decode, prufer_encode};
26+
pub use self::strongly_connected_components::StronglyConnectedComponents;
2527
pub use self::topological_sort::topological_sort;
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
Tarjan's algorithm to find Strongly Connected Components (SCCs):
3+
It runs in O(n + m) (so it is optimal) and as a by-product, it returns the
4+
components in some (reverse) topologically sorted order.
5+
6+
We assume that graph is represented using (compressed) adjacency matrix
7+
and its vertices are numbered from 1 to n. If this is not the case, one
8+
can use `src/graph/graph_enumeration.rs` to convert their graph.
9+
*/
10+
11+
pub struct StronglyConnectedComponents {
12+
// The number of the SCC the vertex is in, starting from 1
13+
pub component: Vec<usize>,
14+
15+
// The discover time of the vertex with minimum discover time reachable
16+
// from this vertex. The MSB of the numbers are used to save whether the
17+
// vertex has been visited (but the MSBs are cleared after
18+
// the algorithm is done)
19+
pub state: Vec<u64>,
20+
21+
// The total number of SCCs
22+
pub num_components: usize,
23+
24+
// The stack of vertices that DFS has seen (used internally)
25+
stack: Vec<usize>,
26+
// Used internally during DFS to know the current discover time
27+
current_time: usize,
28+
}
29+
30+
// Some functions to help with DRY and code readability
31+
const NOT_DONE: u64 = 1 << 63;
32+
33+
#[inline]
34+
fn set_done(vertex_state: &mut u64) {
35+
*vertex_state ^= NOT_DONE;
36+
}
37+
38+
#[inline]
39+
fn is_in_stack(vertex_state: u64) -> bool {
40+
vertex_state != 0 && (vertex_state & NOT_DONE) != 0
41+
}
42+
43+
#[inline]
44+
fn is_unvisited(vertex_state: u64) -> bool {
45+
vertex_state == NOT_DONE
46+
}
47+
48+
#[inline]
49+
fn get_discover_time(vertex_state: u64) -> u64 {
50+
vertex_state ^ NOT_DONE
51+
}
52+
53+
impl StronglyConnectedComponents {
54+
pub fn new(mut num_vertices: usize) -> Self {
55+
num_vertices += 1; // Vertices are numbered from 1, not 0
56+
StronglyConnectedComponents {
57+
component: vec![0; num_vertices],
58+
state: vec![NOT_DONE; num_vertices],
59+
num_components: 0,
60+
stack: vec![],
61+
current_time: 1,
62+
}
63+
}
64+
fn dfs(&mut self, v: usize, adj: &[Vec<usize>]) -> u64 {
65+
let mut min_disc = self.current_time as u64;
66+
// self.state[v] = NOT_DONE + min_disc
67+
self.state[v] ^= min_disc;
68+
self.current_time += 1;
69+
self.stack.push(v);
70+
71+
for &u in adj[v].iter() {
72+
if is_unvisited(self.state[u]) {
73+
min_disc = std::cmp::min(self.dfs(u, adj), min_disc);
74+
} else if is_in_stack(self.state[u]) {
75+
min_disc = std::cmp::min(get_discover_time(self.state[u]), min_disc);
76+
}
77+
}
78+
79+
// No vertex with a lower discovery time is reachable from this one
80+
// So it should be "the head" of a new SCC.
81+
if min_disc == get_discover_time(self.state[v]) {
82+
self.num_components += 1;
83+
loop {
84+
let u = self.stack.pop().unwrap();
85+
self.component[u] = self.num_components;
86+
set_done(&mut self.state[u]);
87+
if u == v {
88+
break;
89+
}
90+
}
91+
}
92+
93+
min_disc
94+
}
95+
pub fn find_components(&mut self, adj: &[Vec<usize>]) {
96+
self.state[0] = 0;
97+
for v in 1..adj.len() {
98+
if is_unvisited(self.state[v]) {
99+
self.dfs(v, adj);
100+
}
101+
}
102+
}
103+
}
104+
105+
#[cfg(test)]
106+
mod tests {
107+
use super::*;
108+
109+
#[test]
110+
fn acyclic() {
111+
let mut sccs = StronglyConnectedComponents::new(5);
112+
let adj = vec![vec![], vec![2, 4], vec![3, 4], vec![5], vec![5], vec![]];
113+
sccs.find_components(&adj);
114+
assert_eq!(sccs.component, vec![0, 5, 4, 2, 3, 1]);
115+
assert_eq!(sccs.state, vec![0, 1, 2, 3, 5, 4]);
116+
assert_eq!(sccs.num_components, 5);
117+
}
118+
119+
#[test]
120+
fn cycle() {
121+
let mut sccs = StronglyConnectedComponents::new(4);
122+
let adj = vec![vec![], vec![2], vec![3], vec![4], vec![1]];
123+
sccs.find_components(&adj);
124+
assert_eq!(sccs.component, vec![0, 1, 1, 1, 1]);
125+
assert_eq!(sccs.state, vec![0, 1, 2, 3, 4]);
126+
assert_eq!(sccs.num_components, 1);
127+
}
128+
129+
#[test]
130+
fn dumbbell() {
131+
let mut sccs = StronglyConnectedComponents::new(6);
132+
let adj = vec![
133+
vec![],
134+
vec![2],
135+
vec![3, 4],
136+
vec![1],
137+
vec![5],
138+
vec![6],
139+
vec![4],
140+
];
141+
sccs.find_components(&adj);
142+
assert_eq!(sccs.component, vec![0, 2, 2, 2, 1, 1, 1]);
143+
assert_eq!(sccs.state, vec![0, 1, 2, 3, 4, 5, 6]);
144+
assert_eq!(sccs.num_components, 2);
145+
}
146+
147+
#[test]
148+
fn connected_dumbbell() {
149+
let mut sccs = StronglyConnectedComponents::new(6);
150+
let adj = vec![
151+
vec![],
152+
vec![2],
153+
vec![3, 4],
154+
vec![1],
155+
vec![5, 1],
156+
vec![6],
157+
vec![4],
158+
];
159+
sccs.find_components(&adj);
160+
assert_eq!(sccs.component, vec![0, 1, 1, 1, 1, 1, 1]);
161+
assert_eq!(sccs.state, vec![0, 1, 2, 3, 4, 5, 6]);
162+
assert_eq!(sccs.num_components, 1);
163+
}
164+
}

0 commit comments

Comments
 (0)