petgraph/algo/scc/
kosaraju_scc.rs

1use crate::visit::VisitMap;
2use alloc::vec::Vec;
3
4use crate::visit::{
5    Dfs, DfsPostOrder, IntoNeighborsDirected, IntoNodeIdentifiers, Reversed, Visitable,
6};
7
8/// Renamed to `kosaraju_scc`.
9#[deprecated(note = "renamed to kosaraju_scc")]
10pub fn scc<G>(g: G) -> Vec<Vec<G::NodeId>>
11where
12    G: IntoNeighborsDirected + Visitable + IntoNodeIdentifiers,
13{
14    kosaraju_scc(g)
15}
16
17/// Compute the *strongly connected components* using [Kosaraju's algorithm][1].
18///
19/// This implementation is iterative and does two passes over the nodes.
20///
21/// # Arguments
22/// * `g`: a directed or undirected graph.
23///
24/// # Returns
25/// Return a vector where each element is a strongly connected component (scc).
26/// The order of node ids within each scc is arbitrary, but the order of
27/// the sccs is their postorder (reverse topological sort).
28///
29/// For an undirected graph, the sccs are simply the connected components.
30///
31/// # Complexity
32/// * Time complexity: **O(|V| + |E|)**.
33/// * Auxiliary space: **O(|V|)**.
34///
35/// where **|V|** is the number of nodes and **|E|** is the number of edges.
36///
37/// # Examples
38///
39/// ```rust
40/// use petgraph::Graph;
41/// use petgraph::algo::kosaraju_scc;
42/// use petgraph::prelude::*;
43///
44/// let mut graph: Graph<i32, (), Directed> = Graph::new();
45/// let a = graph.add_node(1);
46/// let b = graph.add_node(2);
47/// let c = graph.add_node(3);
48/// let d = graph.add_node(4);
49/// let e = graph.add_node(5);
50/// let f = graph.add_node(6);
51///
52/// graph.extend_with_edges(&[
53///     (a, b), (b, c), (c, a),  // First SCC: a -> b -> c -> a
54///     (d, e), (e, f), (f, d),  // Second SCC: d -> e -> f -> d
55///     (c, d),                  // Connection between SCCs
56/// ]);
57///
58/// // Graph structure:
59/// // a ---> b       e ---> f
60/// // ↑     ↓       ↑      |
61/// // └---- c --->  d <----┘
62///
63/// let sccs = kosaraju_scc(&graph);
64/// assert_eq!(sccs.len(), 2); // Two strongly connected components
65///
66/// // Each SCC contains 3 nodes
67/// assert_eq!(sccs[0].len(), 3);
68/// assert_eq!(sccs[1].len(), 3);
69/// ```
70///
71/// For a simple directed acyclic graph (DAG):
72///
73/// ```rust
74/// use petgraph::Graph;
75/// use petgraph::algo::kosaraju_scc;
76/// use petgraph::prelude::*;
77///
78/// let mut dag: Graph<&str, (), Directed> = Graph::new();
79/// let a = dag.add_node("A");
80/// let b = dag.add_node("B");
81/// let c = dag.add_node("C");
82///
83/// dag.extend_with_edges(&[(a, b), (b, c)]);
84/// // A -> B -> C
85///
86/// let sccs = kosaraju_scc(&dag);
87/// assert_eq!(sccs.len(), 3); // Each node is its own SCC
88///
89/// // Each SCC contains exactly one node
90/// for scc in &sccs {
91///     assert_eq!(scc.len(), 1);
92/// }
93/// ```
94///
95/// [1]: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
96pub fn kosaraju_scc<G>(g: G) -> Vec<Vec<G::NodeId>>
97where
98    G: IntoNeighborsDirected + Visitable + IntoNodeIdentifiers,
99{
100    let mut dfs = DfsPostOrder::empty(g);
101
102    // First phase, reverse dfs pass, compute finishing times.
103    // http://stackoverflow.com/a/26780899/161659
104    let mut finish_order = Vec::with_capacity(0);
105    for i in g.node_identifiers() {
106        if dfs.discovered.is_visited(&i) {
107            continue;
108        }
109
110        dfs.move_to(i);
111        while let Some(nx) = dfs.next(Reversed(g)) {
112            finish_order.push(nx);
113        }
114    }
115
116    let mut dfs = Dfs::from_parts(dfs.stack, dfs.discovered);
117    dfs.reset(g);
118    let mut sccs = Vec::new();
119
120    // Second phase
121    // Process in decreasing finishing time order
122    for i in finish_order.into_iter().rev() {
123        if dfs.discovered.is_visited(&i) {
124            continue;
125        }
126        // Move to the leader node `i`.
127        dfs.move_to(i);
128        let mut scc = Vec::new();
129        while let Some(nx) = dfs.next(g) {
130            scc.push(nx);
131        }
132        sccs.push(scc);
133    }
134    sccs
135}