Skip to content

Commit b0f8ccf

Browse files
Add Breadth-First Search (BFS) (#152)
1 parent b3d0b78 commit b0f8ccf

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* [Lasso](https://github.com/TheAlgorithms/R/blob/HEAD/data_preprocessing/lasso.r)
3636

3737
## Graph Algorithms
38+
* [Breadth First Search](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/breadth_first_search.r)
3839
* [Depth First Search](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/depth_first_search.r)
3940

4041
## Mathematics
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Breadth-First Search (BFS) Algorithm
2+
#
3+
# BFS is a graph traversal algorithm that explores all vertices at the current depth
4+
# before moving to vertices at the next depth level. It uses a queue data structure.
5+
#
6+
# Time Complexity: O(V + E) where V is vertices and E is edges
7+
# Space Complexity: O(V) for the visited array and queue
8+
#
9+
# Input: An adjacency list representation of a graph and a starting vertex
10+
# Output: The order in which vertices are visited during BFS traversal
11+
12+
# BFS function using queue (implemented with vector)
13+
breadth_first_search <- function(graph, start_vertex) {
14+
# Get all vertices in the graph
15+
all_vertices <- unique(c(names(graph), unlist(graph)))
16+
17+
# Initialize visited array and queue
18+
visited <- rep(FALSE, max(all_vertices))
19+
names(visited) <- 1:max(all_vertices)
20+
queue <- c(start_vertex)
21+
result <- c()
22+
23+
# Mark starting vertex as visited
24+
visited[start_vertex] <- TRUE
25+
26+
while (length(queue) > 0) {
27+
# Dequeue vertex from front of queue
28+
vertex <- queue[1]
29+
queue <- queue[-1]
30+
result <- c(result, vertex)
31+
32+
# Add all unvisited neighbors to queue
33+
if (as.character(vertex) %in% names(graph)) {
34+
for (neighbor in graph[[as.character(vertex)]]) {
35+
if (!visited[neighbor]) {
36+
visited[neighbor] <- TRUE
37+
queue <- c(queue, neighbor)
38+
}
39+
}
40+
}
41+
}
42+
43+
return(result)
44+
}
45+
46+
# BFS to find shortest path between two vertices
47+
bfs_shortest_path <- function(graph, start_vertex, end_vertex) {
48+
# Get all vertices in the graph
49+
all_vertices <- unique(c(names(graph), unlist(graph)))
50+
51+
# Initialize visited array, queue, and parent tracking
52+
visited <- rep(FALSE, max(all_vertices))
53+
names(visited) <- 1:max(all_vertices)
54+
parent <- rep(-1, max(all_vertices))
55+
names(parent) <- 1:max(all_vertices)
56+
queue <- c(start_vertex)
57+
58+
# Mark starting vertex as visited
59+
visited[start_vertex] <- TRUE
60+
61+
while (length(queue) > 0) {
62+
# Dequeue vertex from front of queue
63+
vertex <- queue[1]
64+
queue <- queue[-1]
65+
66+
# If we reached the target vertex, reconstruct path
67+
if (vertex == end_vertex) {
68+
path <- c()
69+
current <- end_vertex
70+
71+
# Backtrack using parent array
72+
while (current != -1) {
73+
path <- c(current, path)
74+
current <- parent[current]
75+
}
76+
77+
return(list(
78+
path = path,
79+
distance = length(path) - 1
80+
))
81+
}
82+
83+
# Add all unvisited neighbors to queue
84+
if (as.character(vertex) %in% names(graph)) {
85+
for (neighbor in graph[[as.character(vertex)]]) {
86+
if (!visited[neighbor]) {
87+
visited[neighbor] <- TRUE
88+
parent[neighbor] <- vertex
89+
queue <- c(queue, neighbor)
90+
}
91+
}
92+
}
93+
}
94+
95+
# No path found
96+
return(list(
97+
path = NULL,
98+
distance = -1
99+
))
100+
}
101+
102+
# BFS to find all vertices at a specific distance
103+
bfs_vertices_at_distance <- function(graph, start_vertex, target_distance) {
104+
# Get all vertices in the graph
105+
all_vertices <- unique(c(names(graph), unlist(graph)))
106+
107+
# Initialize visited array, queue with distances
108+
visited <- rep(FALSE, max(all_vertices))
109+
names(visited) <- 1:max(all_vertices)
110+
queue <- list(list(vertex = start_vertex, distance = 0))
111+
vertices_at_distance <- c()
112+
113+
# Mark starting vertex as visited
114+
visited[start_vertex] <- TRUE
115+
116+
while (length(queue) > 0) {
117+
# Dequeue vertex with distance from front of queue
118+
current <- queue[[1]]
119+
queue <- queue[-1]
120+
vertex <- current$vertex
121+
distance <- current$distance
122+
123+
# If current distance matches target, add to result
124+
if (distance == target_distance) {
125+
vertices_at_distance <- c(vertices_at_distance, vertex)
126+
}
127+
128+
# Don't explore further if we've reached target distance
129+
if (distance < target_distance) {
130+
# Add all unvisited neighbors to queue
131+
if (as.character(vertex) %in% names(graph)) {
132+
for (neighbor in graph[[as.character(vertex)]]) {
133+
if (!visited[neighbor]) {
134+
visited[neighbor] <- TRUE
135+
queue <- c(queue, list(list(vertex = neighbor, distance = distance + 1)))
136+
}
137+
}
138+
}
139+
}
140+
}
141+
142+
return(vertices_at_distance)
143+
}
144+
145+
# Example usage and testing
146+
cat("=== Breadth-First Search (BFS) Algorithm ===\n")
147+
148+
# Create a sample graph as adjacency list
149+
# Graph structure:
150+
# 1
151+
# / \
152+
# 2 3
153+
# / \ \
154+
# 4 5 6
155+
graph <- list(
156+
"1" = c(2, 3),
157+
"2" = c(4, 5),
158+
"3" = c(6),
159+
"4" = c(),
160+
"5" = c(),
161+
"6" = c()
162+
)
163+
164+
cat("Graph structure (adjacency list):\n")
165+
for (vertex in names(graph)) {
166+
cat("Vertex", vertex, "-> [", paste(graph[[vertex]], collapse = ", "), "]\n")
167+
}
168+
169+
# Test BFS traversal
170+
cat("\nBFS starting from vertex 1:\n")
171+
result <- breadth_first_search(graph, 1)
172+
cat("Traversal order:", paste(result, collapse = " -> "), "\n")
173+
174+
# Test BFS from different starting vertex
175+
cat("\nBFS starting from vertex 2:\n")
176+
result_from_2 <- breadth_first_search(graph, 2)
177+
cat("Traversal order:", paste(result_from_2, collapse = " -> "), "\n")
178+
179+
# Test shortest path finding
180+
cat("\n=== Shortest Path Finding ===\n")
181+
path_result <- bfs_shortest_path(graph, 1, 5)
182+
if (!is.null(path_result$path)) {
183+
cat("Shortest path from 1 to 5:", paste(path_result$path, collapse = " -> "), "\n")
184+
cat("Distance:", path_result$distance, "\n")
185+
} else {
186+
cat("No path found from 1 to 5\n")
187+
}
188+
189+
# Test vertices at specific distance
190+
cat("\n=== Vertices at Specific Distance ===\n")
191+
vertices_dist_2 <- bfs_vertices_at_distance(graph, 1, 2)
192+
cat("Vertices at distance 2 from vertex 1:", paste(vertices_dist_2, collapse = ", "), "\n")
193+
194+
# Example with a more complex connected graph
195+
cat("\n=== Example with More Complex Graph ===\n")
196+
complex_graph <- list(
197+
"1" = c(2, 3),
198+
"2" = c(1, 4, 5),
199+
"3" = c(1, 6),
200+
"4" = c(2, 7),
201+
"5" = c(2, 8),
202+
"6" = c(3, 9),
203+
"7" = c(4),
204+
"8" = c(5),
205+
"9" = c(6)
206+
)
207+
208+
cat("Complex graph BFS starting from vertex 1:\n")
209+
complex_result <- breadth_first_search(complex_graph, 1)
210+
cat("Traversal order:", paste(complex_result, collapse = " -> "), "\n")
211+
212+
# Test shortest path in complex graph
213+
path_complex <- bfs_shortest_path(complex_graph, 1, 9)
214+
if (!is.null(path_complex$path)) {
215+
cat("Shortest path from 1 to 9:", paste(path_complex$path, collapse = " -> "), "\n")
216+
cat("Distance:", path_complex$distance, "\n")
217+
}

0 commit comments

Comments
 (0)