Enhance docs, add more tests in Kosaraju (#5966)

This commit is contained in:
Hardik Pawar
2024-10-24 11:08:08 +05:30
committed by GitHub
parent 13be2501c2
commit 578e5a73df
2 changed files with 143 additions and 86 deletions

View File

@ -5,82 +5,91 @@ import java.util.List;
import java.util.Stack;
/**
* Java program that implements Kosaraju Algorithm.
* @author <a href="https://github.com/shivu2002a">Shivanagouda S A</a>
* This class implements the Kosaraju Algorithm to find all the Strongly Connected Components (SCCs)
* of a directed graph. Kosaraju's algorithm runs in linear time and leverages the concept that
* the SCCs of a directed graph remain the same in its transpose (reverse) graph.
*
* <p>
* 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.
* A strongly connected component (SCC) of a directed graph is a subgraph where every vertex
* is reachable from every other vertex in the subgraph. The Kosaraju algorithm is particularly
* efficient for finding SCCs because it performs two Depth First Search (DFS) passes on the
* graph and its transpose.
* </p>
*
* <p><strong>Algorithm:</strong></p>
* <ol>
* <li>Perform DFS on the original graph and push nodes to a stack in the order of their finishing time.</li>
* <li>Generate the transpose (reversed edges) of the original graph.</li>
* <li>Perform DFS on the transpose graph, using the stack from the first DFS. Each DFS run on the transpose graph gives a SCC.</li>
* </ol>
*
* <p><strong>Example Graph:</strong></p>
* <pre>
* 0 <--- 2 -------> 3 -------- > 4 ---- > 7
* | ^ | ^ ^
* | / | \ /
* | / | \ /
* v / v \ /
* 1 5 --> 6
* </pre>
*
* <p><strong>SCCs in the example:</strong></p>
* <ul>
* <li>{0, 1, 2}</li>
* <li>{3}</li>
* <li>{4, 5, 6}</li>
* <li>{7}</li>
* </ul>
*
* <p>The order of nodes in an SCC does not matter because every node in an SCC is reachable from every other node within the same SCC.</p>
*
* <p><strong>Graph Transpose Example:</strong></p>
* <pre>
* 0 ---> 2 <------- 3 <------- 4 <------ 7
* ^ / ^ \ /
* | / | \ /
* | / | \ /
* | v | v v
* 1 5 <--- 6
* </pre>
*
* The SCCs of this transpose graph are the same as the original graph.
*/
public class Kosaraju {
// Sort edges according to lowest finish time
Stack<Integer> stack = new Stack<Integer>();
// Stack to sort edges by the lowest finish time (used in the first DFS)
private final Stack<Integer> stack = new Stack<>();
// Store each component
// Store each strongly connected component
private List<Integer> scc = new ArrayList<>();
// All the strongly connected components
private List<List<Integer>> sccsList = new ArrayList<>();
// List of all SCCs
private final List<List<Integer>> sccsList = new ArrayList<>();
/**
* Main function to perform Kosaraju's Algorithm.
* Steps:
* 1. Sort nodes by the lowest finishing time
* 2. Create the transpose (reverse edges) of the original graph
* 3. Find SCCs by performing DFS on the transpose graph
* 4. Return the list of SCCs
*
* @param v Node count
* @param list Adjacency list of graph
* @return List of SCCs
* @param v the number of vertices in the graph
* @param list the adjacency list representing the directed graph
* @return a list of SCCs where each SCC is a list of vertices
*/
public List<List<Integer>> kosaraju(int v, List<List<Integer>> list) {
sortEdgesByLowestFinishTime(v, list);
List<List<Integer>> transposeGraph = createTransposeMatrix(v, list);
findStronglyConnectedComponents(v, transposeGraph);
return sccsList;
}
/**
* Performs DFS on the original graph to sort nodes by their finishing times.
* @param v the number of vertices in the graph
* @param list the adjacency list representing the original graph
*/
private void sortEdgesByLowestFinishTime(int v, List<List<Integer>> list) {
int[] vis = new int[v];
for (int i = 0; i < v; i++) {
@ -90,8 +99,14 @@ public class Kosaraju {
}
}
/**
* Creates the transpose (reverse) of the original graph.
* @param v the number of vertices in the graph
* @param list the adjacency list representing the original graph
* @return the adjacency list representing the transposed graph
*/
private List<List<Integer>> createTransposeMatrix(int v, List<List<Integer>> list) {
var transposeGraph = new ArrayList<List<Integer>>(v);
List<List<Integer>> transposeGraph = new ArrayList<>(v);
for (int i = 0; i < v; i++) {
transposeGraph.add(new ArrayList<>());
}
@ -104,14 +119,14 @@ public class Kosaraju {
}
/**
*
* @param v Node count
* @param transposeGraph Transpose of the given adjacency list
* Finds the strongly connected components (SCCs) by performing DFS on the transposed graph.
* @param v the number of vertices in the graph
* @param transposeGraph the adjacency list representing the transposed graph
*/
public void findStronglyConnectedComponents(int v, List<List<Integer>> transposeGraph) {
int[] vis = new int[v];
while (!stack.isEmpty()) {
var node = stack.pop();
int node = stack.pop();
if (vis[node] == 0) {
dfs2(node, vis, transposeGraph);
sccsList.add(scc);
@ -120,7 +135,12 @@ public class Kosaraju {
}
}
// Dfs to store the nodes in order of lowest finish time
/**
* Performs DFS on the original graph and pushes nodes onto the stack in order of their finish time.
* @param node the current node being visited
* @param vis array to keep track of visited nodes
* @param list the adjacency list of the graph
*/
private void dfs(int node, int[] vis, List<List<Integer>> list) {
vis[node] = 1;
for (Integer neighbour : list.get(node)) {
@ -131,7 +151,12 @@ public class Kosaraju {
stack.push(node);
}
// Dfs to find all the nodes of each strongly connected component
/**
* Performs DFS on the transposed graph to find the strongly connected components.
* @param node the current node being visited
* @param vis array to keep track of visited nodes
* @param list the adjacency list of the transposed graph
*/
private void dfs2(int node, int[] vis, List<List<Integer>> list) {
vis[node] = 1;
for (Integer neighbour : list.get(node)) {