Skip to content

Commit 747bb49

Browse files
bidirectional_bfs (#226)
1 parent c458e2f commit 747bb49

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# ==============================================================
2+
# Bidirectional Breadth-First Search (BFS) Shortest Path Algorithm
3+
# ==============================================================
4+
#
5+
# Description:
6+
# Finds the shortest path between a source and target in an
7+
# unweighted graph using Bidirectional BFS.
8+
#
9+
# Time Complexity: O(b^(d/2)) — much faster than normal BFS O(b^d)
10+
# Space Complexity: O(V)
11+
#
12+
# Input:
13+
# graph - adjacency list (list of integer vectors)
14+
# source - integer (starting vertex)
15+
# target - integer (destination vertex)
16+
#
17+
# Output:
18+
# A list containing:
19+
# path - vector of vertices representing the path
20+
# distance - number of edges in the shortest path
21+
# found - logical flag (TRUE if path found, else FALSE)
22+
#
23+
# Example usage at bottom of file.
24+
# ==============================================================
25+
26+
bidirectional_bfs <- function(graph, source, target) {
27+
if (source == target) {
28+
return(list(path = c(source), distance = 0, found = TRUE))
29+
}
30+
31+
# Initialize BFS from both ends
32+
visited_from_source <- setNames(rep(FALSE, length(graph)), names(graph))
33+
visited_from_target <- setNames(rep(FALSE, length(graph)), names(graph))
34+
35+
parent_from_source <- rep(NA, length(graph))
36+
parent_from_target <- rep(NA, length(graph))
37+
38+
queue_source <- c(source)
39+
queue_target <- c(target)
40+
41+
visited_from_source[source] <- TRUE
42+
visited_from_target[target] <- TRUE
43+
44+
meeting_node <- NA
45+
46+
# Function to check intersection
47+
get_intersection <- function() {
48+
common <- which(visited_from_source & visited_from_target)
49+
if (length(common) > 0) return(common[1])
50+
return(NA)
51+
}
52+
53+
# Main loop
54+
while (length(queue_source) > 0 && length(queue_target) > 0) {
55+
# Expand one level from source side
56+
next_queue <- c()
57+
for (u in queue_source) {
58+
for (v in graph[[as.character(u)]]) {
59+
if (!visited_from_source[v]) {
60+
visited_from_source[v] <- TRUE
61+
parent_from_source[v] <- u
62+
next_queue <- c(next_queue, v)
63+
}
64+
}
65+
}
66+
queue_source <- next_queue
67+
68+
# Check intersection
69+
meeting_node <- get_intersection()
70+
if (!is.na(meeting_node)) break
71+
72+
# Expand one level from target side
73+
next_queue <- c()
74+
for (u in queue_target) {
75+
for (v in graph[[as.character(u)]]) {
76+
if (!visited_from_target[v]) {
77+
visited_from_target[v] <- TRUE
78+
parent_from_target[v] <- u
79+
next_queue <- c(next_queue, v)
80+
}
81+
}
82+
}
83+
queue_target <- next_queue
84+
85+
# Check intersection again
86+
meeting_node <- get_intersection()
87+
if (!is.na(meeting_node)) break
88+
}
89+
90+
if (is.na(meeting_node)) {
91+
return(list(path = NULL, distance = Inf, found = FALSE))
92+
}
93+
94+
# Reconstruct path from source → meeting_node
95+
path1 <- c()
96+
node <- meeting_node
97+
while (!is.na(node)) {
98+
path1 <- c(node, path1)
99+
node <- parent_from_source[node]
100+
}
101+
102+
# Reconstruct path from meeting_node → target
103+
path2 <- c()
104+
node <- parent_from_target[meeting_node]
105+
while (!is.na(node)) {
106+
path2 <- c(path2, node)
107+
node <- parent_from_target[node]
108+
}
109+
110+
full_path <- c(path1, path2)
111+
return(list(path = full_path, distance = length(full_path) - 1, found = TRUE))
112+
}
113+
114+
# ==============================================================
115+
# Example Usage and Test
116+
# ==============================================================
117+
118+
cat("=== Bidirectional BFS Shortest Path ===\n")
119+
120+
# Example Graph (Unweighted)
121+
# 1 -- 2 -- 3
122+
# | |
123+
# 4 -- 5 -- 6
124+
125+
graph <- list(
126+
"1" = c(2, 4),
127+
"2" = c(1, 3, 5),
128+
"3" = c(2, 6),
129+
"4" = c(1, 5),
130+
"5" = c(2, 4, 6),
131+
"6" = c(3, 5)
132+
)
133+
134+
cat("Graph adjacency list:\n")
135+
for (v in names(graph)) {
136+
cat("Vertex", v, "-> [", paste(graph[[v]], collapse = ", "), "]\n")
137+
}
138+
139+
cat("\nRunning Bidirectional BFS from 1 to 6...\n")
140+
result <- bidirectional_bfs(graph, 1, 6)
141+
142+
if (result$found) {
143+
cat("Shortest Path Found!\n")
144+
cat("Path:", paste(result$path, collapse = " -> "), "\n")
145+
cat("Distance:", result$distance, "\n")
146+
} else {
147+
cat("No path found between source and target.\n")
148+
}
149+
return(list(
150+
distances = distances,
151+
predecessor = predecessor,
152+
found = found
153+
))

0 commit comments

Comments
 (0)