mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-06 00:54:32 +08:00
Enhance docs, add more tests in Kosaraju
(#5966)
This commit is contained in:
@ -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)) {
|
||||
|
Reference in New Issue
Block a user