diff --git a/graph_algorithms/bridge_detector.r b/graph_algorithms/bridge_detector.r new file mode 100644 index 00000000..ffd6eeac --- /dev/null +++ b/graph_algorithms/bridge_detector.r @@ -0,0 +1,226 @@ +# bridge_detector.r +# Bridge Detection Algorithm in R using Tarjan's Algorithm +# Finds all critical edges (bridges) in an undirected graph. +# A bridge is an edge whose removal increases the number of connected components. +# +# Algorithm details: +# - Uses DFS traversal with discovery time and low-link values +# - disc[v]: Discovery time of vertex v in DFS +# - low[v]: Earliest discovered vertex reachable from v's subtree +# - Bridge condition: For edge (u,v), if low[v] > disc[u], then (u,v) is a bridge +# - Time complexity: O(V + E) where V = vertices, E = edges +# - Space complexity: O(V) for visited, discovery, and low-link arrays + +library(R6) + +BridgeDetector <- R6Class( + "BridgeDetector", + + public = list( + vertices = NULL, + graph = NULL, + time_counter = NULL, + + initialize = function(n_vertices) { + "Initialize the bridge detector with specified number of vertices" + if (!is.numeric(n_vertices) || n_vertices < 0 || n_vertices != round(n_vertices)) { + stop("Number of vertices must be a non-negative integer") + } + self$vertices <- n_vertices + self$graph <- vector("list", n_vertices) + self$time_counter <- 0 + + for (i in seq_len(n_vertices)) { + self$graph[[i]] <- integer(0) + } + }, + + add_edge = function(u, v) { + "Add an undirected edge between vertices u and v (0-indexed)" + if (!is.numeric(u) || !is.numeric(v) || + u < 0 || v < 0 || + u >= self$vertices || v >= self$vertices || + u != round(u) || v != round(v)) { + stop("Vertex indices must be integers in range [0, n_vertices-1]") + } + u_idx <- u + 1 + v_idx <- v + 1 + self$graph[[u_idx]] <- c(self$graph[[u_idx]], v_idx) + self$graph[[v_idx]] <- c(self$graph[[v_idx]], u_idx) + }, + + find_bridges = function() { + "Find all bridges in the graph using Tarjan's algorithm" + visited <- rep(FALSE, self$vertices) + disc <- rep(Inf, self$vertices) + low <- rep(Inf, self$vertices) + parent <- rep(-1, self$vertices) + bridges <- list() + + self$time_counter <- 0 + + for (v in seq_len(self$vertices)) { + if (!visited[v]) { + res <- private$dfs_bridge(v, visited, disc, low, parent, bridges) + visited <- res$visited + disc <- res$disc + low <- res$low + parent <- res$parent + bridges <- res$bridges + } + } + return(bridges) + }, + + print_graph = function() { + "Print adjacency list of the graph (0-indexed)" + cat("Graph Adjacency List:\n") + for (i in seq_len(self$vertices)) { + neighbors <- if (length(self$graph[[i]]) > 0) { + paste(self$graph[[i]] - 1, collapse = ", ") + } else { + "none" + } + cat(sprintf("Vertex %d: %s\n", i - 1, neighbors)) + } + } + ), + + private = list( + dfs_bridge = function(u, visited, disc, low, parent, bridges) { + visited[u] <- TRUE + disc[u] <- self$time_counter + low[u] <- self$time_counter + self$time_counter <- self$time_counter + 1 + + for (v_idx in self$graph[[u]]) { + if (!visited[v_idx]) { + parent[v_idx] <- u + res <- private$dfs_bridge(v_idx, visited, disc, low, parent, bridges) + visited <- res$visited + disc <- res$disc + low <- res$low + parent <- res$parent + bridges <- res$bridges + + low[u] <- min(low[u], low[v_idx]) + + if (low[v_idx] > disc[u]) { + bridges[[length(bridges) + 1]] <- c(u - 1, v_idx - 1) + } + } else if (v_idx != parent[u]) { + low[u] <- min(low[u], disc[v_idx]) + } + } + + return(list( + visited = visited, + disc = disc, + low = low, + parent = parent, + bridges = bridges + )) + } + ) +) + +# Demonstration +demonstrate_bridge_detection <- function() { + cat("=== Bridge Detection Algorithm Demo ===\n\n") + + # Example 1 + cat("Example 1: Simple network with bridges\n") + cat("Graph: 0-1-2-3\n") + cat(" | |\n") + cat(" 4 5\n\n") + + detector1 <- BridgeDetector$new(6) + detector1$add_edge(0, 1) + detector1$add_edge(1, 2) + detector1$add_edge(2, 3) + detector1$add_edge(0, 4) + detector1$add_edge(2, 5) + + bridges1 <- detector1$find_bridges() + cat("Bridges found:\n") + for (b in bridges1) { + cat(sprintf(" (%d, %d)\n", b[1], b[2])) + } + cat("All edges are critical - removing any disconnects the network.\n\n") + + # Example 2 + cat("Example 2: Network with cycle (no bridges)\n") + cat("Graph: 0-1-2\n") + cat(" | |\n") + cat(" +---+\n\n") + + detector2 <- BridgeDetector$new(3) + detector2$add_edge(0, 1) + detector2$add_edge(1, 2) + detector2$add_edge(2, 0) + + bridges2 <- detector2$find_bridges() + if (length(bridges2) == 0) { + cat("Bridges found: None\n") + cat("The cycle provides redundancy - no single edge is critical.\n\n") + } + + # Example 3 + cat("Example 3: Complex network topology\n") + detector3 <- BridgeDetector$new(7) + detector3$add_edge(0, 1) + detector3$add_edge(1, 2) + detector3$add_edge(2, 0) + detector3$add_edge(1, 3) + detector3$add_edge(3, 4) + detector3$add_edge(4, 5) + detector3$add_edge(5, 6) + detector3$add_edge(6, 4) + + bridges3 <- detector3$find_bridges() + cat("Bridges found:\n") + for (b in bridges3) { + cat(sprintf(" (%d, %d)\n", b[1], b[2])) + } + cat("Edge (1,3) connects two robust sub-networks.\n\n") + + # Example 4: Testing print_graph + cat("Example 4: Viewing graph structure\n") + detector4 <- BridgeDetector$new(4) + detector4$add_edge(0, 1) + detector4$add_edge(1, 2) + detector4$add_edge(2, 3) + detector4$print_graph() + cat("\n") + + # Example 5: Edge cases + cat("Example 5: Edge cases\n") + + # Empty graph + detector5 <- BridgeDetector$new(0) + bridges5 <- detector5$find_bridges() + cat("Empty graph bridges: ", length(bridges5), "\n") + + # Single edge + detector6 <- BridgeDetector$new(2) + detector6$add_edge(0, 1) + bridges6 <- detector6$find_bridges() + cat("Single edge graph bridges:\n") + for (b in bridges6) { + cat(sprintf(" (%d, %d)\n", b[1], b[2])) + } + + # Disconnected components + detector7 <- BridgeDetector$new(4) + detector7$add_edge(0, 1) + detector7$add_edge(2, 3) + bridges7 <- detector7$find_bridges() + cat("Disconnected components bridges:\n") + for (b in bridges7) { + cat(sprintf(" (%d, %d)\n", b[1], b[2])) + } + cat("\n") +} + +# Run demo +demonstrate_bridge_detection() \ No newline at end of file