diff --git a/DIRECTORY.md b/DIRECTORY.md index 9285bf66..9add7101 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -57,6 +57,8 @@ * [Depth First Search](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/depth_first_search.r) * [Dijkstra Shortest Path](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/dijkstra_shortest_path.r) * [Floyd Warshall](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/floyd_warshall.r) + * [Johnson Shortest Paths](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/johnson_shortest_paths.r) + * [Kosaraju Scc](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/kosaraju_scc.r) ## Linked List Algorithms diff --git a/graph_algorithms/kosaraju_scc.r b/graph_algorithms/kosaraju_scc.r new file mode 100644 index 00000000..d64d3c24 --- /dev/null +++ b/graph_algorithms/kosaraju_scc.r @@ -0,0 +1,378 @@ +# Kosaraju's Algorithm for Finding Strongly Connected Components +# +# Kosaraju's algorithm is used to find all strongly connected components (SCCs) in a directed graph. +# A strongly connected component is a maximal set of vertices such that there is a path from +# each vertex to every other vertex in the component. +# +# Algorithm Steps: +# 1. Perform DFS on the original graph and store vertices in finishing order (stack) +# 2. Create transpose graph (reverse all edge directions) +# 3. Perform DFS on transpose graph in the order of decreasing finish times +# +# Time Complexity: O(V + E) where V is vertices and E is edges +# Space Complexity: O(V) for visited arrays and recursion stack +# +# Author: Contributor for TheAlgorithms/R +# Applications: Social network analysis, web crawling, circuit design verification + +# Helper function: DFS to fill stack with finishing times +dfs_fill_order <- function(graph, vertex, visited, stack) { + # Mark current vertex as visited + visited[vertex] <- TRUE + + # Visit all adjacent vertices + if (as.character(vertex) %in% names(graph)) { + for (neighbor in graph[[as.character(vertex)]]) { + if (!visited[neighbor]) { + result <- dfs_fill_order(graph, neighbor, visited, stack) + stack <- result$stack + visited <- result$visited + } + } + } + + # Push current vertex to stack (finishing time) + stack <- c(stack, vertex) + + return(list(visited = visited, stack = stack)) +} + +# Helper function: DFS to collect vertices in current SCC +dfs_collect_scc <- function(transpose_graph, vertex, visited, current_scc) { + # Mark current vertex as visited + visited[vertex] <- TRUE + current_scc <- c(current_scc, vertex) + + # Visit all adjacent vertices in transpose graph + if (as.character(vertex) %in% names(transpose_graph)) { + for (neighbor in transpose_graph[[as.character(vertex)]]) { + if (!visited[neighbor]) { + result <- dfs_collect_scc(transpose_graph, neighbor, visited, current_scc) + visited <- result$visited + current_scc <- result$current_scc + } + } + } + + return(list(visited = visited, current_scc = current_scc)) +} + +# Function to create transpose graph (reverse all edges) +create_transpose_graph <- function(graph) { + # Initialize empty transpose graph + transpose_graph <- list() + + # Get all vertices + all_vertices <- unique(c(names(graph), unlist(graph))) + + # Initialize empty adjacency lists for all vertices + for (vertex in all_vertices) { + transpose_graph[[as.character(vertex)]] <- c() + } + + # Reverse all edges + for (vertex in names(graph)) { + for (neighbor in graph[[vertex]]) { + # Add edge from neighbor to vertex (reverse direction) + transpose_graph[[as.character(neighbor)]] <- + c(transpose_graph[[as.character(neighbor)]], as.numeric(vertex)) + } + } + + # Remove empty adjacency lists + transpose_graph <- transpose_graph[lengths(transpose_graph) > 0 | names(transpose_graph) %in% names(graph)] + + return(transpose_graph) +} + +# Main Kosaraju's Algorithm function +kosaraju_scc <- function(graph) { + #' Kosaraju's Algorithm for Strongly Connected Components + #' + #' @param graph A named list representing adjacency list of directed graph + #' Format: list("1" = c(2, 3), "2" = c(3), "3" = c()) + #' Keys are vertex names (as strings), values are vectors of adjacent vertices + #' + #' @return A list containing: + #' - scc_list: List of strongly connected components (each is a vector of vertices) + #' - scc_count: Number of strongly connected components + #' - vertex_to_scc: Named vector mapping each vertex to its SCC number + #' - transpose_graph: The transpose graph used in algorithm + + # Input validation + if (!is.list(graph)) { + stop("Graph must be a list representing adjacency list") + } + + if (length(graph) == 0) { + return(list(scc_list = list(), scc_count = 0, vertex_to_scc = c(), transpose_graph = list())) + } + + # Get all vertices in the graph + all_vertices <- unique(c(names(graph), unlist(graph))) + max_vertex <- max(all_vertices) + + # Initialize visited array for first DFS + visited <- rep(FALSE, max_vertex) + names(visited) <- 1:max_vertex + stack <- c() + + # Step 1: Fill vertices in stack according to their finishing times + cat("Step 1: Performing DFS to determine finishing order...\n") + for (vertex in all_vertices) { + if (!visited[vertex]) { + result <- dfs_fill_order(graph, vertex, visited, stack) + visited <- result$visited + stack <- result$stack + } + } + + cat("Finishing order (stack):", rev(stack), "\n") + + # Step 2: Create transpose graph + cat("Step 2: Creating transpose graph...\n") + transpose_graph <- create_transpose_graph(graph) + + # Step 3: Perform DFS on transpose graph in order of decreasing finish times + cat("Step 3: Finding SCCs in transpose graph...\n") + visited <- rep(FALSE, max_vertex) + names(visited) <- 1:max_vertex + + scc_list <- list() + scc_count <- 0 + vertex_to_scc <- rep(NA, max_vertex) + names(vertex_to_scc) <- 1:max_vertex + + # Process vertices in reverse finishing order + for (vertex in rev(stack)) { + if (!visited[vertex]) { + scc_count <- scc_count + 1 + result <- dfs_collect_scc(transpose_graph, vertex, visited, c()) + visited <- result$visited + current_scc <- sort(result$current_scc) + + scc_list[[scc_count]] <- current_scc + + # Map vertices to their SCC number + for (v in current_scc) { + vertex_to_scc[v] <- scc_count + } + + cat("SCC", scc_count, ":", current_scc, "\n") + } + } + + # Filter vertex_to_scc to only include vertices that exist in graph + vertex_to_scc <- vertex_to_scc[all_vertices] + + return(list( + scc_list = scc_list, + scc_count = scc_count, + vertex_to_scc = vertex_to_scc, + transpose_graph = transpose_graph + )) +} + +# Print function for SCC results +print_scc_results <- function(result) { + cat("\n=== KOSARAJU'S ALGORITHM RESULTS ===\n") + cat("Number of Strongly Connected Components:", result$scc_count, "\n\n") + + for (i in 1:result$scc_count) { + cat("SCC", i, ":", result$scc_list[[i]], "\n") + } + + cat("\nVertex to SCC mapping:\n") + for (vertex in names(result$vertex_to_scc)) { + if (!is.na(result$vertex_to_scc[vertex])) { + cat("Vertex", vertex, "-> SCC", result$vertex_to_scc[vertex], "\n") + } + } +} + +# Function to visualize graph structure (text-based) +print_graph <- function(graph, title = "Graph") { + cat("\n=== ", title, " ===\n") + if (length(graph) == 0) { + cat("Empty graph\n") + return() + } + + for (vertex in names(graph)) { + if (length(graph[[vertex]]) > 0) { + cat("Vertex", vertex, "->", graph[[vertex]], "\n") + } else { + cat("Vertex", vertex, "-> (no outgoing edges)\n") + } + } + + # Also show vertices with no outgoing edges + all_vertices <- unique(c(names(graph), unlist(graph))) + vertices_with_no_outgoing <- setdiff(all_vertices, names(graph)) + for (vertex in vertices_with_no_outgoing) { + cat("Vertex", vertex, "-> (no outgoing edges)\n") + } +} + +# ============================================================================== +# EXAMPLES AND TEST CASES +# ============================================================================== + +run_kosaraju_examples <- function() { + cat("=================================================================\n") + cat("KOSARAJU'S ALGORITHM - STRONGLY CONNECTED COMPONENTS EXAMPLES\n") + cat("=================================================================\n\n") + + # Example 1: Simple graph with 2 SCCs + cat("EXAMPLE 1: Simple Directed Graph with 2 SCCs\n") + cat("-----------------------------------------------------------------\n") + + # Graph: 1 -> 2 -> 3 -> 1 (SCC: {1,2,3}) and 4 -> 5, 5 -> 4 (SCC: {4,5}) + # Also: 2 -> 4 (bridge between SCCs) + graph1 <- list( + "1" = c(2), + "2" = c(3, 4), + "3" = c(1), + "4" = c(5), + "5" = c(4) + ) + + print_graph(graph1, "Example 1 - Original Graph") + result1 <- kosaraju_scc(graph1) + print_scc_results(result1) + + cat("\n=================================================================\n") + cat("EXAMPLE 2: Linear Chain (No Cycles)\n") + cat("-----------------------------------------------------------------\n") + + # Graph: 1 -> 2 -> 3 -> 4 (Each vertex is its own SCC) + graph2 <- list( + "1" = c(2), + "2" = c(3), + "3" = c(4), + "4" = c() + ) + + print_graph(graph2, "Example 2 - Linear Chain") + result2 <- kosaraju_scc(graph2) + print_scc_results(result2) + + cat("\n=================================================================\n") + cat("EXAMPLE 3: Complex Graph with Multiple SCCs\n") + cat("-----------------------------------------------------------------\n") + + # More complex graph with 3 SCCs + # SCC 1: {1, 2, 3} SCC 2: {4, 5, 6} SCC 3: {7} + graph3 <- list( + "1" = c(2), + "2" = c(3, 4), + "3" = c(1), + "4" = c(5), + "5" = c(6), + "6" = c(4, 7), + "7" = c() + ) + + print_graph(graph3, "Example 3 - Complex Graph") + result3 <- kosaraju_scc(graph3) + print_scc_results(result3) + + cat("\n=================================================================\n") + cat("EXAMPLE 4: Single Strongly Connected Component\n") + cat("-----------------------------------------------------------------\n") + + # Complete cycle: 1 -> 2 -> 3 -> 4 -> 1 + graph4 <- list( + "1" = c(2), + "2" = c(3), + "3" = c(4), + "4" = c(1) + ) + + print_graph(graph4, "Example 4 - Single SCC") + result4 <- kosaraju_scc(graph4) + print_scc_results(result4) + + cat("\n=================================================================\n") + cat("EXAMPLE 5: Disconnected Graph\n") + cat("-----------------------------------------------------------------\n") + + # Two separate components: {1 -> 2 -> 1} and {3 -> 4 -> 3} + graph5 <- list( + "1" = c(2), + "2" = c(1), + "3" = c(4), + "4" = c(3) + ) + + print_graph(graph5, "Example 5 - Disconnected Graph") + result5 <- kosaraju_scc(graph5) + print_scc_results(result5) + + cat("\n=================================================================\n") + cat("PRACTICAL APPLICATION: Social Network Analysis\n") + cat("-----------------------------------------------------------------\n") + + cat("In social networks, SCCs represent groups of people who can\n") + cat("all reach each other through mutual connections. This is useful for:\n") + cat("- Community detection\n") + cat("- Information spread analysis\n") + cat("- Influence maximization\n") + cat("- Network segmentation\n\n") + + # Example social network (simplified) + social_network <- list( + "Alice" = c("Bob"), + "Bob" = c("Charlie", "David"), + "Charlie" = c("Alice"), # Forms cycle Alice->Bob->Charlie->Alice + "David" = c("Eve"), + "Eve" = c("David"), # Forms cycle David->Eve->David + "Frank" = c() # Isolated node + ) + + cat("Social Network Example:\n") + print_graph(social_network, "Social Network Graph") + + # Note: This will work but vertex names will be converted to numbers + cat("Note: Algorithm works with numeric vertices. For named vertices,\n") + cat("you would need to create a mapping between names and numbers.\n\n") + + cat("=================================================================\n") + cat("END OF EXAMPLES\n") + cat("=================================================================\n") +} + +# Utility function to convert named graph to numeric +convert_named_to_numeric_graph <- function(named_graph) { + # Get unique vertex names + all_names <- unique(c(names(named_graph), unlist(named_graph))) + + # Create name to number mapping + name_to_num <- setNames(seq_along(all_names), all_names) + num_to_name <- setNames(all_names, seq_along(all_names)) + + # Convert graph + numeric_graph <- list() + for (vertex_name in names(named_graph)) { + vertex_num <- name_to_num[vertex_name] + neighbors <- named_graph[[vertex_name]] + numeric_neighbors <- name_to_num[neighbors] + numeric_graph[[as.character(vertex_num)]] <- numeric_neighbors + } + + return(list( + graph = numeric_graph, + name_to_num = name_to_num, + num_to_name = num_to_name + )) +} + +# Examples are available but not run automatically to avoid side effects +# To run examples, execute: run_kosaraju_examples() +if (interactive()) { + cat("Loading Kosaraju's Strongly Connected Components Algorithm...\n") + cat("Run 'run_kosaraju_examples()' to see examples and test cases.\n") +} + +# Uncomment the following line to run examples automatically: +# run_kosaraju_examples() \ No newline at end of file