RedEnginePress logo
RedEnginePress
AlgorithmsLanguagesPlaygroundAbout

Kosaraju Scc

S
# 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()