Skip to main content
BFS Pattern

What is BFS?

Breadth-First Search explores all neighbors at the current depth before moving to nodes at the next depth level. It uses a queue and guarantees the shortest path in unweighted graphs.
Quick Recognition: Shortest path in unweighted graphs, level-order traversal, finding nearest target. Keywords: “minimum steps”, “shortest path”, “level by level”.

Pattern Recognition Checklist

Use BFS When

  • Finding shortest path (unweighted)
  • Level-order tree traversal
  • Finding nearest target
  • State space exploration (puzzles)
  • Multi-source shortest distances

Don't Use When

  • Weighted graph (use Dijkstra)
  • Need to explore deep paths first
  • Memory is very constrained
  • Preorder/inorder/postorder needed

BFS vs DFS Quick Reference

AspectBFSDFS
Data StructureQueue (FIFO)Stack/Recursion (LIFO)
Shortest PathYes (unweighted)No
Space ComplexityO(width)O(height)
Use CaseNearest, level-orderPaths, cycles, connectivity
Traversal OrderLevel by levelDeep first

When to Use

Shortest Path

Finding minimum steps in unweighted graphs

Level-Order Traversal

Processing tree/graph by levels

Nearest Neighbor

Finding closest target from source

State Space Search

Puzzle solving, game states

Pattern Variations

1. Tree Level-Order Traversal

from collections import deque

def level_order(root):
    """Return nodes level by level"""
    if not root:
        return []
    
    result = []
    queue = deque([root])
    
    while queue:
        level_size = len(queue)
        current_level = []
        
        for _ in range(level_size):
            node = queue.popleft()
            current_level.append(node.val)
            
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        result.append(current_level)
    
    return result

2. Graph BFS (Shortest Path)

from collections import deque

def shortest_path(graph, start, end):
    """Find shortest path in unweighted graph"""
    if start == end:
        return 0
    
    queue = deque([(start, 0)])  # (node, distance)
    visited = {start}
    
    while queue:
        node, dist = queue.popleft()
        
        for neighbor in graph[node]:
            if neighbor == end:
                return dist + 1
            
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append((neighbor, dist + 1))
    
    return -1  # No path found

3. Grid BFS

from collections import deque

def shortest_path_grid(grid):
    """Find shortest path from top-left to bottom-right"""
    if not grid or grid[0][0] == 1:
        return -1
    
    rows, cols = len(grid), len(grid[0])
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    
    queue = deque([(0, 0, 1)])  # (row, col, distance)
    visited = {(0, 0)}
    
    while queue:
        r, c, dist = queue.popleft()
        
        if r == rows - 1 and c == cols - 1:
            return dist
        
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            
            if 0 <= nr < rows and 0 <= nc < cols:
                if grid[nr][nc] == 0 and (nr, nc) not in visited:
                    visited.add((nr, nc))
                    queue.append((nr, nc, dist + 1))
    
    return -1

4. Multi-Source BFS

from collections import deque

def walls_and_gates(rooms):
    """Fill each empty room with distance to nearest gate"""
    if not rooms:
        return
    
    INF = 2147483647
    GATE = 0
    rows, cols = len(rooms), len(rooms[0])
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    
    # Start BFS from all gates simultaneously
    queue = deque()
    for r in range(rows):
        for c in range(cols):
            if rooms[r][c] == GATE:
                queue.append((r, c))
    
    while queue:
        r, c = queue.popleft()
        
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            
            if 0 <= nr < rows and 0 <= nc < cols:
                if rooms[nr][nc] == INF:  # Empty room
                    rooms[nr][nc] = rooms[r][c] + 1
                    queue.append((nr, nc))

5. Word Ladder (State Space BFS)

from collections import deque

def word_ladder(begin_word, end_word, word_list):
    """Find shortest transformation sequence length"""
    word_set = set(word_list)
    if end_word not in word_set:
        return 0
    
    queue = deque([(begin_word, 1)])
    visited = {begin_word}
    
    while queue:
        word, length = queue.popleft()
        
        if word == end_word:
            return length
        
        # Try changing each character
        for i in range(len(word)):
            for c in 'abcdefghijklmnopqrstuvwxyz':
                new_word = word[:i] + c + word[i+1:]
                
                if new_word in word_set and new_word not in visited:
                    visited.add(new_word)
                    queue.append((new_word, length + 1))
    
    return 0

Classic Problems

Pattern: Process nodes level by levelKey: Track level size before processing
Pattern: Multi-source BFS with time trackingKey: Start from all rotten oranges simultaneously
Pattern: State space BFSKey: Each word is a state, edges are single-char changes
Pattern: Multi-source BFS from target cellsKey: Start from 0s to find distance to nearest 0

Common Mistakes

Avoid These Pitfalls:
  1. Visiting before enqueueing: Mark visited when adding to queue, not when processing
  2. Forgetting to track distance: Include distance in queue tuple or use level-by-level
  3. Not handling disconnected components: BFS from single source won’t reach all nodes
  4. Wrong direction iteration: Process all current level before incrementing distance

Interview Problems by Company

ProblemCompanyKey Concept
Level Order TraversalAll FAANGTree BFS
Rotting OrangesAmazon, MetaMulti-source BFS
Number of IslandsAll FAANGGrid BFS/DFS
Open the LockGoogleState space BFS
Shortest Path in MatrixMetaGrid BFS

Interview Tips

Script for interviews:
  1. “I need shortest path in unweighted graph, so I’ll use BFS.”
  2. “I’ll use a queue starting from the source.”
  3. “For each level, I process all current nodes before moving deeper.”
  4. “I mark nodes visited when enqueuing to avoid duplicates.”
  5. “Time is O(V + E), space is O(V) for the queue.”
from collections import deque

def bfs(start, target, graph):
    queue = deque([(start, 0)])  # (node, distance)
    visited = {start}
    
    while queue:
        node, dist = queue.popleft()
        
        if node == target:
            return dist
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)  # Mark before enqueue!
                queue.append((neighbor, dist + 1))
    
    return -1  # Not found
For problems like “Rotting Oranges” or “01 Matrix”:
  1. Add ALL sources to initial queue
  2. Process level by level
  3. All sources expand simultaneously
  4. First to reach a cell = shortest distance

Practice Problems

ProblemDifficultyLink
Binary Tree Level OrderMediumLeetCode 102
Rotting OrangesMediumLeetCode 994
Word LadderHardLeetCode 127
Shortest Path in Binary MatrixMediumLeetCode 1091
Open the LockMediumLeetCode 752

Practice Roadmap

1

Day 1: Tree BFS

  • Solve: Level Order Traversal, Right Side View
  • Focus: Level-by-level processing
2

Day 2: Grid BFS

  • Solve: Number of Islands, Rotting Oranges
  • Focus: 4-directional movement, multi-source
3

Day 3: State Space BFS

  • Solve: Open the Lock, Minimum Genetic Mutation
  • Focus: State as node, transformation as edge
4

Day 4: Advanced BFS

  • Solve: Word Ladder, Sliding Puzzle
  • Focus: Complex state representations
Interview Tip: When you need shortest path in unweighted graph or level-by-level processing, BFS is the answer.