Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 226 additions & 0 deletions graph_algorithms/bridge_detector.r
Original file line number Diff line number Diff line change
@@ -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()