diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java
index de50044256c6..91974ba13319 100644
--- a/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java
+++ b/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java
@@ -5,66 +5,73 @@
import java.util.Stack;
/**
- * Java program that implements Tarjan's Algorithm.
- * @author Shivanagouda S A
+ * Java program that implements Tarjan's Algorithm to find Strongly Connected Components (SCCs) in a directed graph.
+ *
*
- * Tarjan's algorithm is a linear time algorithm to find the strongly connected components of a
-directed graph, which, from here onwards will be referred as SCC.
-
- * A graph is said to be strongly connected if every vertex is reachable from every other vertex.
-The SCCs of a directed graph form a partition into subgraphs that are themselves strongly
-connected. Single node is always a SCC.
-
- * Example:
-0 --------> 1 -------> 3 --------> 4
-^ /
-| /
-| /
-| /
-| /
-| /
-| /
-| /
-| /
-| /
-|V
-2
-
-For the above graph, the SCC list goes as follows:
-1, 2, 0
-3
-4
-
-We can also see that order of the nodes in an SCC doesn't matter since they are in cycle.
-
-{@summary}
-Tarjan's Algorithm:
- * DFS search produces a DFS tree
- * Strongly Connected Components form subtrees of the DFS tree.
- * If we can find the head of these subtrees, we can get all the nodes in that subtree (including
-the head) and that will be one SCC.
- * There is no back edge from one SCC to another (here can be cross edges, but they will not be
-used).
-
- * Kosaraju Algorithm aims at doing the same but uses two DFS traversalse whereas Tarjan’s
-algorithm does the same in a single DFS, which leads to much lower constant factors in the latter.
-
+ * Tarjan's algorithm is a linear time algorithm (O(V + E)) that identifies the SCCs of a directed graph.
+ * An SCC is a maximal subgraph where every vertex is reachable from every other vertex within the subgraph.
+ *
+ *
Algorithm Overview:
+ *
+ * - DFS Search: A depth-first search (DFS) is performed on the graph to generate a DFS tree.
+ * - Identification of SCCs: SCCs correspond to subtrees within this DFS tree.
+ * - Low-Link Values: For each node, a low-link value is maintained, which indicates the earliest visited
+ * vertex (the one with the minimum insertion time) that can be reached from that subtree.
+ * - Stack Usage: Nodes are stored in a stack during DFS. When an SCC is identified, nodes are popped from
+ * the stack until the head of the SCC is reached.
+ *
+ *
+ *
+ * Example of a directed graph:
+ *
+ * 0 --------> 1 -------> 3 --------> 4
+ * ^ /
+ * | /
+ * | /
+ * | /
+ * | /
+ * | /
+ * | /
+ * | /
+ * | /
+ * | /
+ * V
+ * 2
+ *
+ *
+ *
+ * For the above graph, the SCC list is as follows:
+ *
+ * - 1, 2, 0
+ * - 3
+ * - 4
+ *
+ * The order of nodes in an SCC does not matter as they form cycles.
+ *
+ * Comparison with Kosaraju's Algorithm:
+ *
+ * Kosaraju's algorithm also identifies SCCs but does so using two DFS traversals.
+ * In contrast, Tarjan's algorithm achieves this in a single DFS traversal, leading to improved performance
+ * in terms of constant factors.
+ *
*/
public class TarjansAlgorithm {
- // Timer for tracking lowtime and insertion time
+ // Timer for tracking low time and insertion time
private int time;
- private final List> sccList = new ArrayList>();
+ // List to store all strongly connected components
+ private final List> sccList = new ArrayList<>();
+ /**
+ * Finds and returns the strongly connected components (SCCs) of the directed graph.
+ *
+ * @param v the number of vertices in the graph
+ * @param graph the adjacency list representation of the graph
+ * @return a list of lists, where each inner list represents a strongly connected component
+ */
public List> stronglyConnectedComponents(int v, List> graph) {
-
- // Initially all vertices as unvisited, insertion and low time are undefined
-
- // insertionTime:Time when a node is visited 1st time while DFS traversal
-
- // lowTime: indicates the earliest visited vertex (the vertex with minimum insertion time)
- // that can be reached from a subtree rooted with a particular node.
+ // Initialize arrays for insertion time and low-link values
int[] lowTime = new int[v];
int[] insertionTime = new int[v];
for (int i = 0; i < v; i++) {
@@ -72,11 +79,11 @@ public List> stronglyConnectedComponents(int v, List
lowTime[i] = -1;
}
- // To check if element is present in stack
+ // Track if vertices are in the stack
boolean[] isInStack = new boolean[v];
- // Store nodes during DFS
- Stack st = new Stack();
+ // Stack to hold nodes during DFS
+ Stack st = new Stack<>();
for (int i = 0; i < v; i++) {
if (insertionTime[i] == -1) {
@@ -87,36 +94,44 @@ public List> stronglyConnectedComponents(int v, List
return sccList;
}
+ /**
+ * A utility function to perform DFS and find SCCs.
+ *
+ * @param u the current vertex being visited
+ * @param lowTime array to keep track of the low-link values
+ * @param insertionTime array to keep track of the insertion times
+ * @param isInStack boolean array indicating if a vertex is in the stack
+ * @param st the stack used for DFS
+ * @param graph the adjacency list representation of the graph
+ */
private void stronglyConnCompsUtil(int u, int[] lowTime, int[] insertionTime, boolean[] isInStack, Stack st, List> graph) {
-
- // Initialize insertion time and lowTime value of current node
+ // Set insertion time and low-link value
insertionTime[u] = time;
lowTime[u] = time;
- time += 1;
+ time++;
- // Push current node into stack
+ // Push current node onto the stack
isInStack[u] = true;
st.push(u);
- // Go through all vertices adjacent to this
+ // Explore adjacent vertices
for (Integer vertex : graph.get(u)) {
- // If the adjacent node is unvisited, do DFS
if (insertionTime[vertex] == -1) {
stronglyConnCompsUtil(vertex, lowTime, insertionTime, isInStack, st, graph);
- // update lowTime for the current node comparing lowtime of adj node
+ // Update low-link value
lowTime[u] = Math.min(lowTime[u], lowTime[vertex]);
} else if (isInStack[vertex]) {
- // If adj node is in stack, update low
+ // Vertex is in the stack; update low-link value
lowTime[u] = Math.min(lowTime[u], insertionTime[vertex]);
}
}
- // If lowtime and insertion time are same, current node is the head of an SCC
- // head node found, get all the nodes in this SCC
+
+ // Check if the current vertex is the root of an SCC
if (lowTime[u] == insertionTime[u]) {
int w = -1;
- var scc = new ArrayList();
+ List scc = new ArrayList<>();
- // Stack has all the nodes of the current SCC
+ // Pop vertices from the stack until the root is found
while (w != u) {
w = st.pop();
scc.add(w);
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java
index dc81d99dd0bf..314cc415815d 100644
--- a/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java
@@ -1,6 +1,6 @@
package com.thealgorithms.datastructures.graphs;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
@@ -9,11 +9,11 @@
public class TarjansAlgorithmTest {
- TarjansAlgorithm tarjansAlgo = new TarjansAlgorithm();
+ private final TarjansAlgorithm tarjansAlgo = new TarjansAlgorithm();
@Test
- public void findStronglyConnectedComps() {
- var v = 5;
+ public void testFindStronglyConnectedComponents() {
+ int v = 5;
var graph = new ArrayList>();
for (int i = 0; i < v; i++) {
graph.add(new ArrayList<>());
@@ -32,23 +32,20 @@ public void findStronglyConnectedComps() {
4
*/
List> expectedResult = new ArrayList<>();
-
- expectedResult.add(Arrays.asList(4));
- expectedResult.add(Arrays.asList(3));
+ expectedResult.add(List.of(4));
+ expectedResult.add(List.of(3));
expectedResult.add(Arrays.asList(2, 1, 0));
- assertTrue(expectedResult.equals(actualResult));
+ assertEquals(expectedResult, actualResult);
}
@Test
- public void findStronglyConnectedCompsShouldGetSingleNodes() {
- // Create a adjacency list of graph
- var n = 8;
+ public void testFindStronglyConnectedComponentsWithSingleNodes() {
+ // Create a graph where each node is its own SCC
+ int n = 8;
var adjList = new ArrayList>(n);
-
for (int i = 0; i < n; i++) {
adjList.add(new ArrayList<>());
}
-
adjList.get(0).add(1);
adjList.get(1).add(2);
adjList.get(2).add(3);
@@ -65,6 +62,71 @@ public void findStronglyConnectedCompsShouldGetSingleNodes() {
7, 6, 5, 4, 3, 2, 1, 0
*/
expectedResult.add(Arrays.asList(7, 6, 5, 4, 3, 2, 1, 0));
- assertTrue(expectedResult.equals(actualResult));
+ assertEquals(expectedResult, actualResult);
+ }
+
+ @Test
+ public void testGraphWithMultipleSCCs() {
+ int v = 6;
+ var graph = new ArrayList>();
+ for (int i = 0; i < v; i++) {
+ graph.add(new ArrayList<>());
+ }
+ graph.get(0).add(1);
+ graph.get(1).add(2);
+ graph.get(2).add(0);
+ graph.get(3).add(4);
+ graph.get(4).add(5);
+ graph.get(5).add(3);
+
+ var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph);
+ List> expectedResult = new ArrayList<>();
+ expectedResult.add(Arrays.asList(2, 1, 0)); // SCC containing 0, 1, 2
+ expectedResult.add(Arrays.asList(5, 4, 3)); // SCC containing 3, 4, 5
+ assertEquals(expectedResult, actualResult);
+ }
+
+ @Test
+ public void testDisconnectedGraph() {
+ int v = 7;
+ var graph = new ArrayList>();
+ for (int i = 0; i < v; i++) {
+ graph.add(new ArrayList<>());
+ }
+ graph.get(0).add(1);
+ graph.get(1).add(0);
+ graph.get(2).add(3);
+ graph.get(3).add(4);
+ graph.get(4).add(2);
+
+ var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph);
+ List> expectedResult = new ArrayList<>();
+ expectedResult.add(Arrays.asList(1, 0)); // SCC containing 0, 1
+ expectedResult.add(Arrays.asList(4, 3, 2)); // SCC containing 2, 3, 4
+ expectedResult.add(List.of(5)); // SCC containing 5
+ expectedResult.add(List.of(6)); // SCC containing 6
+ assertEquals(expectedResult, actualResult);
+ }
+
+ @Test
+ public void testSingleNodeGraph() {
+ int v = 1;
+ var graph = new ArrayList>();
+ graph.add(new ArrayList<>());
+
+ var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph);
+ List> expectedResult = new ArrayList<>();
+ expectedResult.add(List.of(0)); // SCC with a single node
+ assertEquals(expectedResult, actualResult);
+ }
+
+ @Test
+ public void testEmptyGraph() {
+ int v = 0;
+ var graph = new ArrayList>();
+
+ var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph);
+ List> expectedResult = new ArrayList<>(); // No SCCs in an empty graph
+ assertEquals(expectedResult, actualResult);
}
}
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy