Breadth-First Search (BFS) Pattern¶
Overview¶
Explore nodes level by level using a queue. Ideal for finding shortest paths in unweighted graphs and exploring neighbors before going deeper.
When to Use¶
- Shortest path in unweighted graph
- Level-order traversal
- Finding minimum steps/moves
- All nodes at distance K
- Connected components
- Bipartite graph checking
- Word ladder / transformation problems
Key Insight: BFS uses a queue to visit nodes level by level, ensuring shortest paths in unweighted graphs.
Template Code¶
Basic BFS on Graph¶
public void bfs(int start, List<List<Integer>> graph) {
boolean[] visited = new boolean[graph.size()];
Queue<Integer> queue = new LinkedList<>();
queue.offer(start);
visited[start] = true;
while (!queue.isEmpty()) {
int node = queue.poll();
System.out.println("Visited: " + node);
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.offer(neighbor);
}
}
}
}
Binary Tree Level Order Traversal¶
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> level = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(level);
}
return result;
}
Shortest Path in Unweighted Graph¶
public int shortestPath(int start, int end, List<List<Integer>> graph) {
boolean[] visited = new boolean[graph.size()];
Queue<int[]> queue = new LinkedList<>(); // [node, distance]
queue.offer(new int[]{start, 0});
visited[start] = true;
while (!queue.isEmpty()) {
int[] current = queue.poll();
int node = current[0], dist = current[1];
if (node == end) {
return dist;
}
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.offer(new int[]{neighbor, dist + 1});
}
}
}
return -1; // No path found
}
Number of Islands (Grid BFS)¶
public int numIslands(char[][] grid) {
int count = 0;
int m = grid.length, n = grid[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
bfsIsland(grid, i, j);
count++;
}
}
}
return count;
}
private void bfsIsland(char[][] grid, int row, int col) {
int m = grid.length, n = grid[0].length;
int[][] directions = {{0,1}, {0,-1}, {1,0}, {-1,0}};
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{row, col});
grid[row][col] = '0';
while (!queue.isEmpty()) {
int[] cell = queue.poll();
for (int[] dir : directions) {
int newRow = cell[0] + dir[0];
int newCol = cell[1] + dir[1];
if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n
&& grid[newRow][newCol] == '1') {
grid[newRow][newCol] = '0';
queue.offer(new int[]{newRow, newCol});
}
}
}
}
Rotting Oranges (Multi-source BFS)¶
public int orangesRotting(int[][] grid) {
int m = grid.length, n = grid[0].length;
Queue<int[]> queue = new LinkedList<>();
int fresh = 0;
// Add all rotten oranges to queue
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 2) {
queue.offer(new int[]{i, j});
} else if (grid[i][j] == 1) {
fresh++;
}
}
}
if (fresh == 0) return 0;
int[][] directions = {{0,1}, {0,-1}, {1,0}, {-1,0}};
int minutes = 0;
while (!queue.isEmpty() && fresh > 0) {
int size = queue.size();
minutes++;
for (int i = 0; i < size; i++) {
int[] cell = queue.poll();
for (int[] dir : directions) {
int r = cell[0] + dir[0];
int c = cell[1] + dir[1];
if (r >= 0 && r < m && c >= 0 && c < n && grid[r][c] == 1) {
grid[r][c] = 2;
fresh--;
queue.offer(new int[]{r, c});
}
}
}
}
return fresh == 0 ? minutes : -1;
}
Word Ladder (Shortest Transformation)¶
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Set<String> wordSet = new HashSet<>(wordList);
if (!wordSet.contains(endWord)) return 0;
Queue<String> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
queue.offer(beginWord);
visited.add(beginWord);
int level = 1;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
String word = queue.poll();
if (word.equals(endWord)) {
return level;
}
// Try all single-character transformations
char[] chars = word.toCharArray();
for (int j = 0; j < chars.length; j++) {
char original = chars[j];
for (char c = 'a'; c <= 'z'; c++) {
if (c == original) continue;
chars[j] = c;
String newWord = new String(chars);
if (wordSet.contains(newWord) && !visited.contains(newWord)) {
queue.offer(newWord);
visited.add(newWord);
}
}
chars[j] = original;
}
}
level++;
}
return 0;
}
Binary Tree Zigzag Level Order¶
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean leftToRight = true;
while (!queue.isEmpty()) {
int size = queue.size();
LinkedList<Integer> level = new LinkedList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (leftToRight) {
level.addLast(node.val);
} else {
level.addFirst(node.val);
}
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(level);
leftToRight = !leftToRight;
}
return result;
}
All Nodes Distance K in Binary Tree¶
public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
// Build parent map
Map<TreeNode, TreeNode> parentMap = new HashMap<>();
buildParentMap(root, null, parentMap);
// BFS from target
Queue<TreeNode> queue = new LinkedList<>();
Set<TreeNode> visited = new HashSet<>();
queue.offer(target);
visited.add(target);
int distance = 0;
while (!queue.isEmpty()) {
if (distance == k) {
List<Integer> result = new ArrayList<>();
for (TreeNode node : queue) {
result.add(node.val);
}
return result;
}
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
// Check left, right, and parent
if (node.left != null && !visited.contains(node.left)) {
visited.add(node.left);
queue.offer(node.left);
}
if (node.right != null && !visited.contains(node.right)) {
visited.add(node.right);
queue.offer(node.right);
}
TreeNode parent = parentMap.get(node);
if (parent != null && !visited.contains(parent)) {
visited.add(parent);
queue.offer(parent);
}
}
distance++;
}
return new ArrayList<>();
}
private void buildParentMap(TreeNode node, TreeNode parent,
Map<TreeNode, TreeNode> map) {
if (node == null) return;
map.put(node, parent);
buildParentMap(node.left, node, map);
buildParentMap(node.right, node, map);
}
Time & Space Complexity¶
| Problem | Time | Space |
|---|---|---|
| Graph BFS | O(V + E) | O(V) |
| Level order | O(n) | O(n) |
| Grid BFS | O(m × n) | O(m × n) |
| Word ladder | O(n × m × 26) | O(n × m) |
| Islands | O(m × n) | O(min(m, n)) |
Common Interview Questions¶
- Binary Tree Level Order Traversal - Basic level order
- Binary Tree Zigzag Level Order - Alternating direction
- Binary Tree Right Side View - Rightmost at each level
- Average of Levels - Mean value per level
- Number of Islands - Count connected components
- Rotting Oranges - Multi-source BFS
- 01 Matrix - Distance to nearest 0
- Word Ladder - Shortest transformation sequence
- Shortest Path in Binary Matrix - Grid navigation
- Open the Lock - State space BFS
- Minimum Knight Moves - Chess knight BFS
- All Nodes Distance K - Tree BFS with parent map
- Clone Graph - BFS graph traversal
- Walls and Gates - Multi-source BFS from gates
Tips & Tricks¶
- Always mark visited before adding to queue (not when polling)
- Use queue size loop for level-based processing
- Multi-source BFS: add all sources initially
- For shortest path, BFS guarantees minimum steps
- Direction arrays make grid traversal cleaner
- For tree levels, track level size at start of each iteration