Imagine you're standing in the middle of a city and want to find the nearest hospital.
You wouldn't first travel through one road for 50 kilometers before checking anything nearby. Instead, you'd first check places one road away, then places two roads away, then three roads away, and so on.
That's the basic idea behind Breadth First Search, or BFS.
Breadth First Search is a graph traversal algorithm that explores vertices level by level, visiting all nearby vertices before moving farther away.
BFS is one of the most important algorithms in DSA because it is especially useful for shortest paths in unweighted graphs, level-order exploration, and minimum-step problems.
How BFS Works
Consider this graph:
0
/ \
1 2
/ \ \
3 4 5If we start BFS from 0, we first visit:
0Then all of its immediate neighbors:
1, 2Then the next level:
3, 4, 5So the traversal is:
0 → 1 → 2 → 3 → 4 → 5The exact order among nodes at the same level can vary depending on the adjacency-list order.
The important part is:
Level 0 → 0
Level 1 → 1, 2
Level 2 → 3, 4, 5That's why it's called Breadth First Search.
BFS Uses a Queue
The most important thing to remember about BFS is:
BFS uses a Queue.
A queue follows:
FIFO
First In, First OutFor example:
10 → 20 → 30
↑
Front10 leaves first because it entered first.
This behavior is exactly what BFS needs to explore nodes level by level.
Why Does BFS Need a Queue?
Let's use:
0
/ \
1 2
/ \
3 4Start with:
Queue:
[0]Remove 0.
Its neighbors are 1 and 2:
Queue:
[1, 2]Remove 1.
Its unvisited neighbors are 3 and 4:
Queue:
[2, 3, 4]Remove 2.
Then:
Queue:
[3, 4]Then 3, then 4.
The queue naturally makes BFS process nodes in the order they were discovered.
The BFS Algorithm
The basic BFS process is:
Start with a vertex
↓
Mark it as visited
↓
Put it into a queue
↓
Remove a vertex from the queue
↓
Visit its unvisited neighbors
↓
Mark them as visited
↓
Add them to the queue
↓
Repeat until queue is emptyIn pseudocode:
BFS(start):
mark start as visited
add start to queue
while queue is not empty:
node = remove from queue
process node
for each neighbor:
if neighbor is not visited:
mark neighbor visited
add neighbor to queueWhy Do We Need visited[]?
Graphs can contain cycles.
Consider:
A ─ B
| |
C ─ DWe can travel:
A → B → D → C → AIf BFS doesn't remember which vertices it has already visited, it could keep adding the same vertices again.
So we use:
boolean[] visited =
new boolean[vertices];When we discover a vertex:
visited[neighbor] = true;This prevents it from being added to the queue repeatedly.
BFS in Java
Let's implement BFS using an adjacency list.
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
class Main {
static void bfs(
int start,
ArrayList<ArrayList<Integer>> graph
) {
boolean[] visited =
new boolean[graph.size()];
Queue<Integer> queue =
new LinkedList<>();
visited[start] = true;
queue.add(start);
while (!queue.isEmpty()) {
int node = queue.remove();
System.out.print(node + " ");
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.add(neighbor);
}
}
}
}
}The most important part is:
visited[start] = true;
queue.add(start);Then:
while (!queue.isEmpty())continues processing until every reachable vertex has been explored.
Building the Graph
Suppose our graph is:
0
/ \
1 2
/ \ \
3 4 5We can create the adjacency list:
int vertices = 6;
ArrayList<ArrayList<Integer>> graph =
new ArrayList<>();
for (int i = 0; i < vertices; i++) {
graph.add(new ArrayList<>());
}For an undirected graph:
graph.get(0).add(1);
graph.get(1).add(0);
graph.get(0).add(2);
graph.get(2).add(0);
graph.get(1).add(3);
graph.get(3).add(1);
graph.get(1).add(4);
graph.get(4).add(1);
graph.get(2).add(5);
graph.get(5).add(2);Then:
bfs(0, graph);One possible output:
0 1 2 3 4 5A Complete BFS Program
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
class Main {
static void addEdge(
ArrayList<ArrayList<Integer>> graph,
int u,
int v
) {
graph.get(u).add(v);
graph.get(v).add(u);
}
static void bfs(
int start,
ArrayList<ArrayList<Integer>> graph
) {
boolean[] visited =
new boolean[graph.size()];
Queue<Integer> queue =
new LinkedList<>();
visited[start] = true;
queue.add(start);
while (!queue.isEmpty()) {
int node = queue.remove();
System.out.print(node + " ");
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.add(neighbor);
}
}
}
}
public static void main(String[] args) {
int vertices = 6;
ArrayList<ArrayList<Integer>> graph =
new ArrayList<>();
for (int i = 0; i < vertices; i++) {
graph.add(new ArrayList<>());
}
addEdge(graph, 0, 1);
addEdge(graph, 0, 2);
addEdge(graph, 1, 3);
addEdge(graph, 1, 4);
addEdge(graph, 2, 5);
bfs(0, graph);
}
}Output:
0 1 2 3 4 5Understanding BFS Step by Step
Let's manually execute the algorithm.
Graph:
0
/ \
1 2
/ \ \
3 4 5Start:
Queue = [0]
Visited = {0}Step 1
Remove 0.
Process → 0Add 1 and 2.
Queue = [1, 2]
Visited = {0, 1, 2}Step 2
Remove 1.
Process → 1Add 3 and 4.
Queue = [2, 3, 4]
Visited = {0, 1, 2, 3, 4}Step 3
Remove 2.
Process → 2Add 5.
Queue = [3, 4, 5]
Visited = {0, 1, 2, 3, 4, 5}Then:
Process → 3
Process → 4
Process → 5Queue becomes empty.
Traversal is complete.
BFS and Levels
One of the most useful properties of BFS is that it naturally processes a graph in levels.
Consider:
0
/ \
1 2
/ \ \
3 4 5
/
6BFS gives:
Level 0 → 0
Level 1 → 1, 2
Level 2 → 3, 4, 5
Level 3 → 6This makes BFS perfect for problems involving:
"How many steps away is this node?"
or:
"Find all nodes at distance K."
BFS for Shortest Path
This is probably the most important application of BFS.
Suppose every edge has the same cost.
A ─ B ─ D
| |
C ───── EIf we start from A, BFS explores based on the number of edges traveled.
Distance 0:
A
Distance 1:
B, C
Distance 2:
D, ETherefore, if we want to reach D:
A → B → Drequires:
2 edgesBFS guarantees the shortest number of edges in an unweighted graph.
BFS with a Distance Array
We can explicitly store the distance of every node from the starting vertex.
static void bfs(
int start,
ArrayList<ArrayList<Integer>> graph
) {
int[] distance =
new int[graph.size()];
boolean[] visited =
new boolean[graph.size()];
Queue<Integer> queue =
new LinkedList<>();
visited[start] = true;
queue.add(start);
while (!queue.isEmpty()) {
int node = queue.remove();
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
distance[neighbor] =
distance[node] + 1;
queue.add(neighbor);
}
}
}
}If:
distance[0] = 0then its direct neighbors have:
distance = 1their neighbors:
distance = 2and so on.
Finding the Actual Shortest Path
Sometimes knowing the distance isn't enough.
We may also want to know the actual path.
For example:
A → B → DWe can use a parent[] array.
Whenever we discover a new node:
parent[neighbor] = node;For example:
parent[B] = A
parent[D] = BThen we can reconstruct the path backward:
D
↑
B
↑
Aand reverse it:
A → B → DThis technique is extremely common in shortest-path problems.
BFS Level-by-Level Traversal
Sometimes we need to process an entire level at once.
For example:
0
/ \
1 2
/ \ \
3 4 5We can process:
Level 0 → [0]
Level 1 → [1, 2]
Level 2 → [3, 4, 5]In Java, we can use the queue size to determine the current level:
while (!queue.isEmpty()) {
int levelSize = queue.size();
for (int i = 0;
i < levelSize;
i++) {
int node = queue.remove();
System.out.print(node + " ");
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.add(neighbor);
}
}
}
System.out.println();
}This produces output such as:
0
1 2
3 4 5This pattern is extremely useful in level-based problems.
BFS on a Grid
BFS isn't limited to traditional graphs.
A 2D grid can also be treated as a graph.
For example:
S . . #
. # . .
. . . EEach cell can represent a vertex.
You can move between neighboring cells.
Then BFS can find the shortest path from:
S → Ewhen each move has equal cost.
This idea is used in:
Maze problems
Grid shortest paths
Robot movement
Game maps
Flood fill
Multi-source BFS
Multi-Source BFS
Normal BFS starts from one source.
But sometimes we have multiple starting points.
For example:
🔥 . . . 🔥
. . . . .
. . . . .Suppose fire spreads one cell per minute.
We can put all fire cells into the queue initially:
Queue:
[fire1, fire2]Then BFS expands from both simultaneously.
This is called Multi-Source BFS.
It is useful for problems involving:
Multiple starting points
Fire spreading
Rotting oranges
Nearest source
Distance from multiple locations
BFS for Connected Components
Suppose:
0 ─ 1 3 ─ 4
| \
2 5There are two components.
We can run BFS from every unvisited vertex:
for each vertex:
if not visited:
BFS(vertex)
componentCount++The first BFS explores:
0, 1, 2The second explores:
3, 4, 5Therefore:
components = 2BFS for Bipartite Graphs
BFS can also help determine whether a graph is bipartite.
The idea is to color vertices using two colors.
For example:
Color A → Red
Color B → BlueWhen we visit a neighbor:
Current = Red
Neighbor = BlueIf we ever discover that two adjacent vertices require the same color, the graph isn't bipartite.
A common BFS implementation uses:
int[] color =
new int[vertices];with values such as:
-1 → Not colored
0 → Color 0
1 → Color 1BFS then alternates colors between adjacent vertices.
BFS vs DFS
This is one of the most important comparisons in DSA.
BFS | DFS |
|---|---|
Breadth first | Depth first |
Uses Queue | Uses Stack/Recursion |
Level by level | Goes deep first |
Excellent for unweighted shortest path | Useful for deep exploration |
Naturally iterative | Naturally recursive |
Good for minimum number of edges | Good for components, cycles, backtracking |
Think of them like this:
BFS:
Start
/ \
↓ ↓
Near Near
↓ ↓
Far Farwhile DFS behaves more like:
Start
↓
A
↓
B
↓
C
↓
BacktrackBFS vs Dijkstra
BFS and Dijkstra can both be used for shortest-path problems, but they solve different cases.
BFS
Use BFS when:
Every edge has equal costFor example:
A ─ B ─ CEvery move costs 1.
Dijkstra
Use Dijkstra when edges have non-negative but potentially different weights:
A --5-- B
| |
2 10
| |
C --1-- DThe edge costs aren't all equal.
So:
Unweighted → BFS
Weighted, non-negative → DijkstraBFS Complexity
For an adjacency-list representation:
Time Complexity → O(V + E)Why?
BFS may visit every vertex:
Vand inspect every edge:
ETherefore:
O(V + E)Space complexity is also:
O(V)for the queue and visited array in the usual adjacency-list implementation, excluding the graph's own storage.
So remember:
Time → O(V + E)
Space → O(V)BFS with an Adjacency Matrix
If we use an adjacency matrix instead of an adjacency list, BFS may need to scan all V possible neighbors for every vertex.
Therefore:
Time → O(V²)So for sparse graphs:
Adjacency List
↓
BFS
↓
O(V + E)is usually preferable.
Common BFS Problems
Once you understand basic BFS, you'll encounter many variations.
1. Shortest Path
Find the minimum number of edges between two vertices.
2. Level Order Traversal
Process nodes one level at a time.
3. Connected Components
Find separate groups in a graph.
4. Multi-Source BFS
Start BFS from multiple sources simultaneously.
5. Bipartite Graph
Color a graph using two colors.
6. Grid Problems
Find the shortest route through a matrix.
7. Minimum Steps
Find the minimum number of moves required to reach a target.
8. Flood Fill
Expand through neighboring cells.
These are all variations of the same fundamental BFS idea.
A Simple Real-Life Example
Imagine a social network:
You
├── Alice
├── Bob
└── CharlieTheir friends:
Alice → David
Bob → Emma
Charlie → FrankIf you're looking for someone who is closest to you, BFS is a natural approach.
First:
Distance 0:
YouThen:
Distance 1:
Alice
Bob
CharlieThen:
Distance 2:
David
Emma
FrankBFS naturally searches outward from you.
The Main Idea
Breadth First Search (BFS) explores a graph level by level.
The fundamental pattern is:
Start
↓
Mark visited
↓
Put into Queue
↓
Remove from Queue
↓
Visit unvisited neighbors
↓
Add them to Queue
↓
RepeatThe most important relationship is:
BFS
↓
Queue
↓
FIFO
↓
Level-by-level explorationIts most important application is:
Unweighted Graph
↓
Shortest Path
↓
BFSAnd its standard complexity with an adjacency list is:
Time → O(V + E)
Space → O(V)Remember these four things:
1. BFS uses a Queue.
2. BFS uses a visited array/set.
3. BFS explores level by level.
4. BFS finds shortest paths in unweighted graphs.If a DSA problem asks for the minimum number of steps, moves, or edges in an unweighted graph, BFS should immediately come to mind.