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
153 changes: 153 additions & 0 deletions graph_algorithms/bidirectional_bfs.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# ==============================================================
# Bidirectional Breadth-First Search (BFS) Shortest Path Algorithm
# ==============================================================
#
# Description:
# Finds the shortest path between a source and target in an
# unweighted graph using Bidirectional BFS.
#
# Time Complexity: O(b^(d/2)) — much faster than normal BFS O(b^d)
# Space Complexity: O(V)
#
# Input:
# graph - adjacency list (list of integer vectors)
# source - integer (starting vertex)
# target - integer (destination vertex)
#
# Output:
# A list containing:
# path - vector of vertices representing the path
# distance - number of edges in the shortest path
# found - logical flag (TRUE if path found, else FALSE)
#
# Example usage at bottom of file.
# ==============================================================

bidirectional_bfs <- function(graph, source, target) {
if (source == target) {
return(list(path = c(source), distance = 0, found = TRUE))
}

# Initialize BFS from both ends
visited_from_source <- setNames(rep(FALSE, length(graph)), names(graph))
visited_from_target <- setNames(rep(FALSE, length(graph)), names(graph))

parent_from_source <- rep(NA, length(graph))
parent_from_target <- rep(NA, length(graph))

queue_source <- c(source)
queue_target <- c(target)

visited_from_source[source] <- TRUE
visited_from_target[target] <- TRUE

meeting_node <- NA

# Function to check intersection
get_intersection <- function() {
common <- which(visited_from_source & visited_from_target)
if (length(common) > 0) return(common[1])
return(NA)
}

# Main loop
while (length(queue_source) > 0 && length(queue_target) > 0) {
# Expand one level from source side
next_queue <- c()
for (u in queue_source) {
for (v in graph[[as.character(u)]]) {
if (!visited_from_source[v]) {
visited_from_source[v] <- TRUE
parent_from_source[v] <- u
next_queue <- c(next_queue, v)
}
}
}
queue_source <- next_queue

# Check intersection
meeting_node <- get_intersection()
if (!is.na(meeting_node)) break

# Expand one level from target side
next_queue <- c()
for (u in queue_target) {
for (v in graph[[as.character(u)]]) {
if (!visited_from_target[v]) {
visited_from_target[v] <- TRUE
parent_from_target[v] <- u
next_queue <- c(next_queue, v)
}
}
}
queue_target <- next_queue

# Check intersection again
meeting_node <- get_intersection()
if (!is.na(meeting_node)) break
}

if (is.na(meeting_node)) {
return(list(path = NULL, distance = Inf, found = FALSE))
}

# Reconstruct path from source → meeting_node
path1 <- c()
node <- meeting_node
while (!is.na(node)) {
path1 <- c(node, path1)
node <- parent_from_source[node]
}

# Reconstruct path from meeting_node → target
path2 <- c()
node <- parent_from_target[meeting_node]
while (!is.na(node)) {
path2 <- c(path2, node)
node <- parent_from_target[node]
}

full_path <- c(path1, path2)
return(list(path = full_path, distance = length(full_path) - 1, found = TRUE))
}

# ==============================================================
# Example Usage and Test
# ==============================================================

cat("=== Bidirectional BFS Shortest Path ===\n")

# Example Graph (Unweighted)
# 1 -- 2 -- 3
# | |
# 4 -- 5 -- 6

graph <- list(
"1" = c(2, 4),
"2" = c(1, 3, 5),
"3" = c(2, 6),
"4" = c(1, 5),
"5" = c(2, 4, 6),
"6" = c(3, 5)
)

cat("Graph adjacency list:\n")
for (v in names(graph)) {
cat("Vertex", v, "-> [", paste(graph[[v]], collapse = ", "), "]\n")
}

cat("\nRunning Bidirectional BFS from 1 to 6...\n")
result <- bidirectional_bfs(graph, 1, 6)

if (result$found) {
cat("Shortest Path Found!\n")
cat("Path:", paste(result$path, collapse = " -> "), "\n")
cat("Distance:", result$distance, "\n")
} else {
cat("No path found between source and target.\n")
}
return(list(
distances = distances,
predecessor = predecessor,
found = found
))
Comment thread
siriak marked this conversation as resolved.