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(" \n BFS 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(" \n BFS 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