Skip to content

Commit 521e9af

Browse files
prashantdubeypngDivyansh1802
andcommitted
feat: Add BreadthFirstSearch algorithm with proper structure and tests
- Removed incorrectly placed .class files and Main.java from root - Added BreadthFirstSearch.java with proper package structure - Implemented generic and integer-based BFS traversal methods - Added comprehensive test coverage in BreadthFirstSearchTest.java - Fixed bug in original BFS where 'src' was used instead of 'current' node - Follows project conventions and coding standards Fixes TheAlgorithms#7158 Co-authored-by: Divyansh1802 <divyanshupadhyay1802@gmail.com>
1 parent 43a7cf7 commit 521e9af

File tree

5 files changed

+353
-0
lines changed

5 files changed

+353
-0
lines changed

Graph$Node.class

-378 Bytes
Binary file not shown.

Graph.class

-2.2 KB
Binary file not shown.

Main.class

-658 Bytes
Binary file not shown.
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.HashSet;
6+
import java.util.LinkedList;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Queue;
10+
import java.util.Set;
11+
12+
/**
13+
* Breadth-First Search (BFS) algorithm for graph traversal.
14+
*
15+
* <p>
16+
* BFS is a graph traversal algorithm that explores all vertices at the present
17+
* depth level before moving on to vertices at the next depth level. It uses a
18+
* queue
19+
* data structure to keep track of vertices to visit.
20+
*
21+
* <p>
22+
* Time Complexity: O(V + E) where V is the number of vertices and E is the
23+
* number of edges.
24+
* Space Complexity: O(V) for the visited set and queue.
25+
*
26+
* <p>
27+
* References:
28+
* <ul>
29+
* <li>https://en.wikipedia.org/wiki/Breadth-first_search</li>
30+
* </ul>
31+
*
32+
* @author Divyansh1802
33+
* @author prashantdubeypng (fixes and refactoring)
34+
*/
35+
public final class BreadthFirstSearch {
36+
37+
private BreadthFirstSearch() {
38+
// Utility class; do not instantiate.
39+
}
40+
41+
/**
42+
* Performs BFS traversal on a graph represented as an adjacency list.
43+
*
44+
* @param adjacencyList the graph represented as a map from vertex to list of
45+
* neighbors
46+
* @param source the starting vertex for traversal
47+
* @param <T> the type of vertices in the graph
48+
* @return a list of vertices in BFS order starting from the source
49+
* @throws IllegalArgumentException if source is null or not in the graph
50+
*/
51+
public static <T> List<T> bfs(Map<T, List<T>> adjacencyList, T source) {
52+
if (source == null) {
53+
throw new IllegalArgumentException("Source vertex cannot be null");
54+
}
55+
if (adjacencyList == null || !adjacencyList.containsKey(source)) {
56+
throw new IllegalArgumentException("Source vertex must exist in the graph");
57+
}
58+
59+
List<T> result = new ArrayList<>();
60+
Set<T> visited = new HashSet<>();
61+
Queue<T> queue = new LinkedList<>();
62+
63+
queue.offer(source);
64+
visited.add(source);
65+
66+
while (!queue.isEmpty()) {
67+
T current = queue.poll();
68+
result.add(current);
69+
70+
List<T> neighbors = adjacencyList.get(current);
71+
if (neighbors != null) {
72+
for (T neighbor : neighbors) {
73+
if (!visited.contains(neighbor)) {
74+
visited.add(neighbor);
75+
queue.offer(neighbor);
76+
}
77+
}
78+
}
79+
}
80+
81+
return result;
82+
}
83+
84+
/**
85+
* Performs BFS traversal on a graph represented using integer vertices.
86+
*
87+
* @param adjacencyList the graph represented as a list of lists of neighbors
88+
* @param numVertices the number of vertices in the graph
89+
* @param source the starting vertex for traversal (0-indexed)
90+
* @return a list of vertices in BFS order starting from the source
91+
* @throws IllegalArgumentException if source is out of bounds
92+
*/
93+
public static List<Integer> bfs(List<List<Integer>> adjacencyList, int numVertices, int source) {
94+
if (source < 0 || source >= numVertices) {
95+
throw new IllegalArgumentException("Source vertex is out of bounds");
96+
}
97+
if (adjacencyList == null) {
98+
throw new IllegalArgumentException("Adjacency list cannot be null");
99+
}
100+
101+
List<Integer> result = new ArrayList<>();
102+
boolean[] visited = new boolean[numVertices];
103+
Queue<Integer> queue = new LinkedList<>();
104+
105+
queue.offer(source);
106+
visited[source] = true;
107+
108+
while (!queue.isEmpty()) {
109+
int current = queue.poll();
110+
result.add(current);
111+
112+
if (current < adjacencyList.size()) {
113+
List<Integer> neighbors = adjacencyList.get(current);
114+
if (neighbors != null) {
115+
for (int neighbor : neighbors) {
116+
if (neighbor >= 0 && neighbor < numVertices && !visited[neighbor]) {
117+
visited[neighbor] = true;
118+
queue.offer(neighbor);
119+
}
120+
}
121+
}
122+
}
123+
}
124+
125+
return result;
126+
}
127+
128+
/**
129+
* Creates an adjacency list graph from edges.
130+
*
131+
* @param numVertices the number of vertices
132+
* @param edges array of edges where each edge is {from, to}
133+
* @param undirected if true, adds edges in both directions
134+
* @return the adjacency list representation of the graph
135+
*/
136+
public static List<List<Integer>> createGraph(int numVertices, int[][] edges, boolean undirected) {
137+
List<List<Integer>> graph = new ArrayList<>();
138+
for (int i = 0; i < numVertices; i++) {
139+
graph.add(new ArrayList<>());
140+
}
141+
142+
for (int[] edge : edges) {
143+
if (edge.length >= 2) {
144+
int from = edge[0];
145+
int to = edge[1];
146+
if (from >= 0 && from < numVertices && to >= 0 && to < numVertices) {
147+
graph.get(from).add(to);
148+
if (undirected) {
149+
graph.get(to).add(from);
150+
}
151+
}
152+
}
153+
}
154+
155+
return graph;
156+
}
157+
158+
/**
159+
* Creates a Map-based adjacency list from string vertices.
160+
*
161+
* @param edges array of edges where each edge is {from, to}
162+
* @param undirected if true, adds edges in both directions
163+
* @return the adjacency list representation as a Map
164+
*/
165+
public static Map<String, List<String>> createStringGraph(String[][] edges, boolean undirected) {
166+
Map<String, List<String>> graph = new HashMap<>();
167+
168+
for (String[] edge : edges) {
169+
if (edge.length >= 2) {
170+
String from = edge[0];
171+
String to = edge[1];
172+
173+
graph.computeIfAbsent(from, k -> new ArrayList<>()).add(to);
174+
if (undirected) {
175+
graph.computeIfAbsent(to, k -> new ArrayList<>()).add(from);
176+
}
177+
}
178+
}
179+
180+
return graph;
181+
}
182+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.util.ArrayList;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
import org.junit.jupiter.api.Test;
12+
13+
/**
14+
* Tests for {@link BreadthFirstSearch}.
15+
*/
16+
class BreadthFirstSearchTest {
17+
18+
@Test
19+
void testBfsWithIntegerGraphSimple() {
20+
// Create a simple graph: 0 -> 1 -> 2
21+
// | |
22+
// v v
23+
// 3 4
24+
int[][] edges = { { 0, 1 }, { 0, 3 }, { 1, 2 }, { 1, 4 } };
25+
List<List<Integer>> graph = BreadthFirstSearch.createGraph(5, edges, false);
26+
27+
List<Integer> result = BreadthFirstSearch.bfs(graph, 5, 0);
28+
29+
assertEquals(5, result.size());
30+
assertEquals(0, result.get(0)); // Source is first
31+
// BFS visits by level: 0 -> [1, 3] -> [2, 4]
32+
assertTrue(result.indexOf(1) < result.indexOf(2)); // 1 before 2
33+
assertTrue(result.indexOf(1) < result.indexOf(4)); // 1 before 4
34+
}
35+
36+
@Test
37+
void testBfsWithUndirectedGraph() {
38+
// Create an undirected graph
39+
int[][] edges = { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 } };
40+
List<List<Integer>> graph = BreadthFirstSearch.createGraph(5, edges, true);
41+
42+
List<Integer> result = BreadthFirstSearch.bfs(graph, 5, 2);
43+
44+
assertEquals(5, result.size());
45+
assertEquals(2, result.get(0)); // Source is first
46+
}
47+
48+
@Test
49+
void testBfsWithDisconnectedGraph() {
50+
// Graph with disconnected components: 0 -> 1, 2 -> 3 (separate components)
51+
int[][] edges = { { 0, 1 }, { 2, 3 } };
52+
List<List<Integer>> graph = BreadthFirstSearch.createGraph(4, edges, false);
53+
54+
List<Integer> result = BreadthFirstSearch.bfs(graph, 4, 0);
55+
56+
// Should only visit reachable vertices from 0
57+
assertEquals(2, result.size());
58+
assertTrue(result.contains(0));
59+
assertTrue(result.contains(1));
60+
}
61+
62+
@Test
63+
void testBfsWithSingleVertex() {
64+
int[][] edges = {};
65+
List<List<Integer>> graph = BreadthFirstSearch.createGraph(1, edges, false);
66+
67+
List<Integer> result = BreadthFirstSearch.bfs(graph, 1, 0);
68+
69+
assertEquals(1, result.size());
70+
assertEquals(0, result.get(0));
71+
}
72+
73+
@Test
74+
void testBfsWithCyclicGraph() {
75+
// Graph with a cycle: 0 -> 1 -> 2 -> 0
76+
int[][] edges = { { 0, 1 }, { 1, 2 }, { 2, 0 } };
77+
List<List<Integer>> graph = BreadthFirstSearch.createGraph(3, edges, false);
78+
79+
List<Integer> result = BreadthFirstSearch.bfs(graph, 3, 0);
80+
81+
assertEquals(3, result.size());
82+
assertEquals(0, result.get(0)); // Source is first
83+
}
84+
85+
@Test
86+
void testBfsWithMapBasedGraph() {
87+
Map<String, List<String>> graph = new HashMap<>();
88+
graph.put("A", List.of("B", "C"));
89+
graph.put("B", List.of("D"));
90+
graph.put("C", List.of("E"));
91+
graph.put("D", new ArrayList<>());
92+
graph.put("E", new ArrayList<>());
93+
94+
List<String> result = BreadthFirstSearch.bfs(graph, "A");
95+
96+
assertEquals(5, result.size());
97+
assertEquals("A", result.get(0)); // Source is first
98+
// B and C should come before D and E
99+
assertTrue(result.indexOf("B") < result.indexOf("D"));
100+
assertTrue(result.indexOf("C") < result.indexOf("E"));
101+
}
102+
103+
@Test
104+
void testBfsWithStringGraphCreation() {
105+
String[][] edges = { { "DELHI", "MUMBAI" }, { "DELHI", "PUNE" }, { "MUMBAI", "GOA" } };
106+
Map<String, List<String>> graph = BreadthFirstSearch.createStringGraph(edges, true);
107+
108+
List<String> result = BreadthFirstSearch.bfs(graph, "DELHI");
109+
110+
assertEquals(4, result.size());
111+
assertEquals("DELHI", result.get(0));
112+
}
113+
114+
@Test
115+
void testBfsThrowsExceptionForNullSource() {
116+
Map<String, List<String>> graph = new HashMap<>();
117+
graph.put("A", List.of("B"));
118+
119+
assertThrows(IllegalArgumentException.class, () -> BreadthFirstSearch.bfs(graph, null));
120+
}
121+
122+
@Test
123+
void testBfsThrowsExceptionForSourceNotInGraph() {
124+
Map<String, List<String>> graph = new HashMap<>();
125+
graph.put("A", List.of("B"));
126+
127+
assertThrows(IllegalArgumentException.class, () -> BreadthFirstSearch.bfs(graph, "X"));
128+
}
129+
130+
@Test
131+
void testBfsThrowsExceptionForInvalidSourceIndex() {
132+
List<List<Integer>> graph = BreadthFirstSearch.createGraph(3, new int[][] {}, false);
133+
134+
assertThrows(IllegalArgumentException.class, () -> BreadthFirstSearch.bfs(graph, 3, -1));
135+
assertThrows(IllegalArgumentException.class, () -> BreadthFirstSearch.bfs(graph, 3, 5));
136+
}
137+
138+
@Test
139+
void testBfsLevelOrderProperty() {
140+
// Test that BFS visits vertices level by level
141+
// 0
142+
// /|\
143+
// 1 2 3
144+
// | |
145+
// 4 5
146+
int[][] edges = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 4 }, { 3, 5 } };
147+
List<List<Integer>> graph = BreadthFirstSearch.createGraph(6, edges, false);
148+
149+
List<Integer> result = BreadthFirstSearch.bfs(graph, 6, 0);
150+
151+
// Level 0: 0
152+
// Level 1: 1, 2, 3
153+
// Level 2: 4, 5
154+
assertEquals(0, result.get(0));
155+
// All level-1 vertices come before level-2 vertices
156+
int level1End = Math.max(result.indexOf(1), Math.max(result.indexOf(2), result.indexOf(3)));
157+
int level2Start = Math.min(result.indexOf(4), result.indexOf(5));
158+
assertTrue(level1End < level2Start);
159+
}
160+
161+
@Test
162+
void testBfsWithEmptyNeighborList() {
163+
Map<String, List<String>> graph = new HashMap<>();
164+
graph.put("A", new ArrayList<>());
165+
166+
List<String> result = BreadthFirstSearch.bfs(graph, "A");
167+
168+
assertEquals(1, result.size());
169+
assertEquals("A", result.get(0));
170+
}
171+
}

0 commit comments

Comments
 (0)