diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java b/src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java new file mode 100644 index 000000000000..b3632b47970d --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java @@ -0,0 +1,144 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * Java program that implements Kosaraju Algorithm. + * @author Shivanagouda S A (https://github.com/shivu2002a) + * + */ + +/** + * Kosaraju algorithm is a linear time algorithm to find the strongly connected components of a + directed graph, which, from here onwards will be referred by SCC. It leverages the fact that the transpose + graph (same graph with all the edges reversed) has exactly the same SCCs as the original graph. + + * 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 <--- 2 -------> 3 -------- > 4 ---- > 7 + | ^ | ^ ^ + | / | \ / + | / | \ / + v / v \ / + 1 5 --> 6 + + For the above graph, the SCC list goes as follows: + 0, 1, 2 + 3 + 4, 5, 6 + 7 + + We can also see that order of the nodes in an SCC doesn't matter since they are in cycle. + + {@summary} + * Kosaraju Algorithm: + 1. Perform DFS traversal of the graph. Push node to stack before returning. This gives edges sorted by lowest finish time. + 2. Find the transpose graph by reversing the edges. + 3. Pop nodes one by one from the stack and again to DFS on the modified graph. + + The transpose graph of the above graph: + 0 ---> 2 <------- 3 <------- 4 <------ 7 + ^ / ^ \ / + | / | \ / + | / | \ / + | v | v v + 1 5 <--- 6 + + We can observe that this graph has the same SCC as that of original graph. + + */ + +public class Kosaraju { + + // Sort edges according to lowest finish time + Stack stack = new Stack(); + + //Store each component + private List scc = new ArrayList<>(); + + //All the strongly connected components + private List> sccsList = new ArrayList<>(); + + /** + * + * @param v Node count + * @param list Adjacency list of graph + * @return List of SCCs + */ + public List> kosaraju(int v, List> list){ + + sortEdgesByLowestFinishTime(v, list); + + List> transposeGraph = createTransposeMatrix(v, list); + + findStronglyConnectedComponents(v, transposeGraph); + + return sccsList; + } + + private void sortEdgesByLowestFinishTime(int v, List> list){ + int vis[] = new int[v]; + for (int i = 0; i < v; i++) { + if(vis[i] == 0){ + dfs(i, vis, list); + } + } + } + + private List> createTransposeMatrix(int v, List> list) { + var transposeGraph = new ArrayList>(v); + for (int i = 0; i < v; i++) { + transposeGraph.add(new ArrayList<>()); + } + for (int i = 0; i < v; i++) { + for (Integer neigh : list.get(i)) { + transposeGraph.get(neigh).add(i); + } + } + return transposeGraph; + } + + /** + * + * @param v Node count + * @param transposeGraph Transpose of the given adjacency list + */ + public void findStronglyConnectedComponents(int v, List> transposeGraph){ + int vis[] = new int[v]; + while (!stack.isEmpty()) { + var node = stack.pop(); + if(vis[node] == 0){ + dfs2(node, vis, transposeGraph); + sccsList.add(scc); + scc = new ArrayList<>(); + } + } + } + + //Dfs to store the nodes in order of lowest finish time + private void dfs(int node, int vis[], List> list){ + vis[node] = 1; + for(Integer neighbour : list.get(node)){ + if(vis[neighbour] == 0) + dfs(neighbour, vis, list); + } + stack.push(node); + } + + //Dfs to find all the nodes of each strongly connected component + private void dfs2(int node, int vis[], List> list){ + vis[node] = 1; + for(Integer neighbour : list.get(node)){ + if(vis[neighbour] == 0) + dfs2(neighbour, vis, list); + } + scc.add(node); + } + +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java new file mode 100644 index 000000000000..5395b04ee732 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java @@ -0,0 +1,81 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +public class KosarajuTest { + + private Kosaraju kosaraju = new Kosaraju(); + + @Test + public void findStronglyConnectedComps() { + //Create a adjacency list of graph + var 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(0); + adjList.get(2).add(3); + adjList.get(3).add(4); + adjList.get(4).add(5); + adjList.get(4).add(7); + adjList.get(5).add(6); + adjList.get(6).add(4); + adjList.get(6).add(7); + + List> actualResult = kosaraju.kosaraju(n, adjList); + List> expectedResult = new ArrayList<>(); + /* + Expected result: + 0, 1, 2 + 3 + 5, 4, 6 + 7 + */ + expectedResult.add(Arrays.asList(1, 2, 0)); + expectedResult.add(Arrays.asList(3)); + expectedResult.add(Arrays.asList(5, 6, 4)); + expectedResult.add(Arrays.asList(7)); + assertTrue(expectedResult.equals(actualResult)); + } + + @Test + public void findStronglyConnectedCompsShouldGetSingleNodes() { + //Create a adjacency list of graph + var 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); + adjList.get(3).add(4); + adjList.get(4).add(5); + adjList.get(5).add(6); + adjList.get(6).add(7); + adjList.get(7).add(0); + + List> actualResult = kosaraju.kosaraju(n, adjList); + List> expectedResult = new ArrayList<>(); + /* + Expected result: + 0, 1, 2, 3, 4, 5, 6, 7 + */ + expectedResult.add(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 0)); + assertTrue(expectedResult.equals(actualResult)); + } + +}