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