11/**
2- * An implementation of Tarjan's Strongly Connected Components algorithm using an adjacency list.
2+ * Implementation of Tarjan's Strongly Connected Components algorithm using an adjacency list.
33 *
44 * <p>Verified against:
55 *
99 * <li>https://www.hackerearth.com/practice/algorithms/graphs/strongly-connected-components/tutorial
1010 * </ul>
1111 *
12- * <p>Time complexity: O(V+E)
12+ * <p>Time: O(V + E)
13+ *
14+ * <p>Space: O(V)
1315 *
1416 * @author William Fiset, william.alexandre.fiset@gmail.com
1517 */
2123
2224public class TarjanSccSolverAdjacencyList {
2325
24- private int n ;
25- private List <List <Integer >> graph ;
26+ private final int n ;
27+ private final List <List <Integer >> graph ;
2628
2729 private boolean solved ;
2830 private int sccCount , id ;
29- private boolean [] visited ;
31+ private boolean [] onStack ;
3032 private int [] ids , low , sccs ;
3133 private Deque <Integer > stack ;
3234
3335 private static final int UNVISITED = -1 ;
3436
37+ /**
38+ * Creates a Tarjan SCC solver for the given directed graph.
39+ *
40+ * @param graph adjacency list representation of a directed graph.
41+ * @throws IllegalArgumentException if the graph is null.
42+ */
3543 public TarjanSccSolverAdjacencyList (List <List <Integer >> graph ) {
36- if (graph == null ) throw new IllegalArgumentException ("Graph cannot be null." );
44+ if (graph == null )
45+ throw new IllegalArgumentException ("Graph cannot be null." );
3746 n = graph .size ();
3847 this .graph = graph ;
3948 }
4049
41- // Returns the number of strongly connected components in the graph.
50+ /** Returns the number of strongly connected components in the graph. */
4251 public int sccCount () {
43- if (!solved ) solve ();
52+ if (!solved )
53+ solve ();
4454 return sccCount ;
4555 }
4656
47- // Get the connected components of this graph. If two indexes
48- // have the same value then they're in the same SCC.
57+ /**
58+ * Returns the SCC id for each node. If two nodes have the same value, they belong to the same
59+ * SCC.
60+ */
4961 public int [] getSccs () {
50- if (!solved ) solve ();
62+ if (!solved )
63+ solve ();
5164 return sccs ;
5265 }
5366
67+ /** Runs Tarjan's algorithm. */
5468 public void solve () {
55- if (solved ) return ;
69+ if (solved )
70+ return ;
5671
5772 ids = new int [n ];
5873 low = new int [n ];
5974 sccs = new int [n ];
60- visited = new boolean [n ];
75+ onStack = new boolean [n ];
6176 stack = new ArrayDeque <>();
6277 Arrays .fill (ids , UNVISITED );
6378
64- for (int i = 0 ; i < n ; i ++) {
65- if (ids [i ] == UNVISITED ) {
79+ for (int i = 0 ; i < n ; i ++)
80+ if (ids [i ] == UNVISITED )
6681 dfs (i );
67- }
68- }
6982
7083 solved = true ;
7184 }
7285
7386 private void dfs (int at ) {
7487 ids [at ] = low [at ] = id ++;
7588 stack .push (at );
76- visited [at ] = true ;
89+ onStack [at ] = true ;
7790
7891 for (int to : graph .get (at )) {
79- if (ids [to ] == UNVISITED ) {
92+ if (ids [to ] == UNVISITED )
8093 dfs (to );
81- }
82- if (visited [to ]) {
94+ if (onStack [to ])
8395 low [at ] = min (low [at ], low [to ]);
84- }
85- /*
86- TODO(william): investigate whether the proper way to update the lowlinks
87- is the following bit of code. From my experience this doesn't seem to
88- matter if the output is placed in a separate output array, but this needs
89- further investigation.
90-
91- if (ids[to] == UNVISITED) {
92- dfs(to);
93- low[at] = min(low[at], low[to]);
94- }
95- if (visited[to]) {
96- low[at] = min(low[at], ids[to]);
97- }
98- */
99-
10096 }
10197
102- // On recursive callback, if we're at the root node (start of SCC)
103- // empty the seen stack until back to root.
98+ // If we're at the root of an SCC, pop all nodes in this component off the stack.
10499 if (ids [at ] == low [at ]) {
105100 for (int node = stack .pop (); ; node = stack .pop ()) {
106- visited [node ] = false ;
101+ onStack [node ] = false ;
107102 sccs [node ] = sccCount ;
108- if (node == at ) break ;
103+ if (node == at )
104+ break ;
109105 }
110106 sccCount ++;
111107 }
112108 }
113109
114- // Initializes adjacency list with n nodes.
110+ /** Creates an adjacency list with n nodes. */
115111 public static List <List <Integer >> createGraph (int n ) {
116112 List <List <Integer >> graph = new ArrayList <>(n );
117- for (int i = 0 ; i < n ; i ++) graph .add (new ArrayList <>());
113+ for (int i = 0 ; i < n ; i ++)
114+ graph .add (new ArrayList <>());
118115 return graph ;
119116 }
120117
121- // Adds a directed edge from node 'from' to node 'to'
118+ /** Adds a directed edge from node 'from' to node 'to'. */
122119 public static void addEdge (List <List <Integer >> graph , int from , int to ) {
123120 graph .get (from ).add (to );
124121 }
125122
126- /* Example usage: */
127-
128- public static void main (String [] arg ) {
123+ public static void main (String [] args ) {
129124 int n = 8 ;
130125 List <List <Integer >> graph = createGraph (n );
131126
@@ -148,15 +143,9 @@ public static void main(String[] arg) {
148143 int [] sccs = solver .getSccs ();
149144 Map <Integer , List <Integer >> multimap = new HashMap <>();
150145 for (int i = 0 ; i < n ; i ++) {
151- if (!multimap .containsKey (sccs [i ])) multimap .put (sccs [i ], new ArrayList <>());
152- multimap .get (sccs [i ]).add (i );
146+ multimap .computeIfAbsent (sccs [i ], k -> new ArrayList <>()).add (i );
153147 }
154148
155- // Prints:
156- // Number of Strongly Connected Components: 3
157- // Nodes: [0, 1, 2] form a Strongly Connected Component.
158- // Nodes: [3, 7] form a Strongly Connected Component.
159- // Nodes: [4, 5, 6] form a Strongly Connected Component.
160149 System .out .printf ("Number of Strongly Connected Components: %d\n " , solver .sccCount ());
161150 for (List <Integer > scc : multimap .values ()) {
162151 System .out .println ("Nodes: " + scc + " form a Strongly Connected Component." );
0 commit comments