55import java .util .Stack ;
66
77/**
8- * Java program that implements Tarjan's Algorithm.
9- * @author <a href="https://github.com/shivu2002a">Shivanagouda S A</a>
8+ * Java program that implements Tarjan's Algorithm to find Strongly Connected Components (SCCs) in a directed graph .
9+ *
1010 * <p>
11- * Tarjan's algorithm is a linear time algorithm to find the strongly connected components of a
12- directed graph, which, from here onwards will be referred as SCC.
13-
14- * A graph is said to be strongly connected if every vertex is reachable from every other vertex.
15- The SCCs of a directed graph form a partition into subgraphs that are themselves strongly
16- connected. Single node is always a SCC.
17-
18- * Example:
19- 0 --------> 1 -------> 3 --------> 4
20- ^ /
21- | /
22- | /
23- | /
24- | /
25- | /
26- | /
27- | /
28- | /
29- | /
30- |V
31- 2
32-
33- For the above graph, the SCC list goes as follows:
34- 1, 2, 0
35- 3
36- 4
37-
38- We can also see that order of the nodes in an SCC doesn't matter since they are in cycle.
39-
40- {@summary}
41- Tarjan's Algorithm:
42- * DFS search produces a DFS tree
43- * Strongly Connected Components form subtrees of the DFS tree.
44- * If we can find the head of these subtrees, we can get all the nodes in that subtree (including
45- the head) and that will be one SCC.
46- * There is no back edge from one SCC to another (here can be cross edges, but they will not be
47- used).
48-
49- * Kosaraju Algorithm aims at doing the same but uses two DFS traversalse whereas Tarjan’s
50- algorithm does the same in a single DFS, which leads to much lower constant factors in the latter.
51-
11+ * Tarjan's algorithm is a linear time algorithm (O(V + E)) that identifies the SCCs of a directed graph.
12+ * An SCC is a maximal subgraph where every vertex is reachable from every other vertex within the subgraph.
13+ *
14+ * <h3>Algorithm Overview:</h3>
15+ * <ul>
16+ * <li>DFS Search: A depth-first search (DFS) is performed on the graph to generate a DFS tree.</li>
17+ * <li>Identification of SCCs: SCCs correspond to subtrees within this DFS tree.</li>
18+ * <li>Low-Link Values: For each node, a low-link value is maintained, which indicates the earliest visited
19+ * vertex (the one with the minimum insertion time) that can be reached from that subtree.</li>
20+ * <li>Stack Usage: Nodes are stored in a stack during DFS. When an SCC is identified, nodes are popped from
21+ * the stack until the head of the SCC is reached.</li>
22+ * </ul>
23+ *
24+ * <p>
25+ * Example of a directed graph:
26+ * <pre>
27+ * 0 --------> 1 -------> 3 --------> 4
28+ * ^ /
29+ * | /
30+ * | /
31+ * | /
32+ * | /
33+ * | /
34+ * | /
35+ * | /
36+ * | /
37+ * | /
38+ * V
39+ * 2
40+ * </pre>
41+ *
42+ * <p>
43+ * For the above graph, the SCC list is as follows:
44+ * <ul>
45+ * <li>1, 2, 0</li>
46+ * <li>3</li>
47+ * <li>4</li>
48+ * </ul>
49+ * The order of nodes in an SCC does not matter as they form cycles.
50+ *
51+ * <h3>Comparison with Kosaraju's Algorithm:</h3>
52+ * <p>
53+ * Kosaraju's algorithm also identifies SCCs but does so using two DFS traversals.
54+ * In contrast, Tarjan's algorithm achieves this in a single DFS traversal, leading to improved performance
55+ * in terms of constant factors.
56+ * </p>
5257 */
5358public class TarjansAlgorithm {
5459
55- // Timer for tracking lowtime and insertion time
60+ // Timer for tracking low time and insertion time
5661 private int time ;
5762
58- private final List <List <Integer >> sccList = new ArrayList <List <Integer >>();
63+ // List to store all strongly connected components
64+ private final List <List <Integer >> sccList = new ArrayList <>();
5965
66+ /**
67+ * Finds and returns the strongly connected components (SCCs) of the directed graph.
68+ *
69+ * @param v the number of vertices in the graph
70+ * @param graph the adjacency list representation of the graph
71+ * @return a list of lists, where each inner list represents a strongly connected component
72+ */
6073 public List <List <Integer >> stronglyConnectedComponents (int v , List <List <Integer >> graph ) {
61-
62- // Initially all vertices as unvisited, insertion and low time are undefined
63-
64- // insertionTime:Time when a node is visited 1st time while DFS traversal
65-
66- // lowTime: indicates the earliest visited vertex (the vertex with minimum insertion time)
67- // that can be reached from a subtree rooted with a particular node.
74+ // Initialize arrays for insertion time and low-link values
6875 int [] lowTime = new int [v ];
6976 int [] insertionTime = new int [v ];
7077 for (int i = 0 ; i < v ; i ++) {
7178 insertionTime [i ] = -1 ;
7279 lowTime [i ] = -1 ;
7380 }
7481
75- // To check if element is present in stack
82+ // Track if vertices are in the stack
7683 boolean [] isInStack = new boolean [v ];
7784
78- // Store nodes during DFS
79- Stack <Integer > st = new Stack <Integer >();
85+ // Stack to hold nodes during DFS
86+ Stack <Integer > st = new Stack <>();
8087
8188 for (int i = 0 ; i < v ; i ++) {
8289 if (insertionTime [i ] == -1 ) {
@@ -87,36 +94,44 @@ public List<List<Integer>> stronglyConnectedComponents(int v, List<List<Integer>
8794 return sccList ;
8895 }
8996
97+ /**
98+ * A utility function to perform DFS and find SCCs.
99+ *
100+ * @param u the current vertex being visited
101+ * @param lowTime array to keep track of the low-link values
102+ * @param insertionTime array to keep track of the insertion times
103+ * @param isInStack boolean array indicating if a vertex is in the stack
104+ * @param st the stack used for DFS
105+ * @param graph the adjacency list representation of the graph
106+ */
90107 private void stronglyConnCompsUtil (int u , int [] lowTime , int [] insertionTime , boolean [] isInStack , Stack <Integer > st , List <List <Integer >> graph ) {
91-
92- // Initialize insertion time and lowTime value of current node
108+ // Set insertion time and low-link value
93109 insertionTime [u ] = time ;
94110 lowTime [u ] = time ;
95- time += 1 ;
111+ time ++ ;
96112
97- // Push current node into stack
113+ // Push current node onto the stack
98114 isInStack [u ] = true ;
99115 st .push (u );
100116
101- // Go through all vertices adjacent to this
117+ // Explore adjacent vertices
102118 for (Integer vertex : graph .get (u )) {
103- // If the adjacent node is unvisited, do DFS
104119 if (insertionTime [vertex ] == -1 ) {
105120 stronglyConnCompsUtil (vertex , lowTime , insertionTime , isInStack , st , graph );
106- // update lowTime for the current node comparing lowtime of adj node
121+ // Update low-link value
107122 lowTime [u ] = Math .min (lowTime [u ], lowTime [vertex ]);
108123 } else if (isInStack [vertex ]) {
109- // If adj node is in stack, update low
124+ // Vertex is in the stack; update low-link value
110125 lowTime [u ] = Math .min (lowTime [u ], insertionTime [vertex ]);
111126 }
112127 }
113- // If lowtime and insertion time are same, current node is the head of an SCC
114- // head node found, get all the nodes in this SCC
128+
129+ // Check if the current vertex is the root of an SCC
115130 if (lowTime [u ] == insertionTime [u ]) {
116131 int w = -1 ;
117- var scc = new ArrayList <Integer >();
132+ List < Integer > scc = new ArrayList <>();
118133
119- // Stack has all the nodes of the current SCC
134+ // Pop vertices from the stack until the root is found
120135 while (w != u ) {
121136 w = st .pop ();
122137 scc .add (w );
0 commit comments