Home
Day 2 · Week 1

Breadth-First Search (BFS)

Level 1 · Fundamentals  ·  2 hrs: ~45 min theory · ~60 min code · ~15 min notes

🧠 Mental Model

BFS explores the graph in concentric rings around the start: all nodes at distance 1, then all at distance 2, and so on. Because it never skips a ring, the first time you reach a node is guaranteed to be via a shortest path — in an unweighted graph.

That single guarantee is why BFS is the go-to for "shortest number of steps / minimum moves" problems. The queue enforces the ring order: FIFO means you finish a whole ring before touching the next.

⚙️ Mechanics

  1. Put the start node in a queue and mark it visited.
  2. Pop the front node, process it, and enqueue all unvisited neighbors — marking them visited as you enqueue.
  3. Repeat until the queue empties.

Traced example

Graph: 0–1, 0–2, 1–3, 2–3, start at 0.

StepQueue (front→back)VisitedDistance found
init[0]{0}0:0
pop 0[1, 2]{0,1,2}1:1, 2:1
pop 1[2, 3]{0,1,2,3}3:2
pop 2[3]3 already visited
pop 3[]done

Two crucial variants

📊 Complexity

MetricValueWhy
TimeO(V + E)Each node enqueued once; each edge examined once.
SpaceO(V)Queue + visited set, worst case holds a full ring.
Use BFS when: shortest path in an unweighted graph, minimum moves, level-by-level processing, or "closest source" queries. Don't use it for weighted shortest paths — that's Dijkstra.

💻 Reference Implementation (Java)

Grid BFS template (the most common interview form) with level counting.

import java.util.*;

/** Shortest steps from (sr,sc) to any target cell in a grid. */
public int bfs(int[][] grid, int sr, int sc) {
    int rows = grid.length, cols = grid[0].length;
    int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}}; // 4-directional
    boolean[][] seen = new boolean[rows][cols];
    Queue<int[]> q = new ArrayDeque<>();

    q.offer(new int[]{sr, sc});
    seen[sr][sc] = true;                 // mark on ENQUEUE, not on pop
    int steps = 0;

    while (!q.isEmpty()) {
        int ringSize = q.size();        // freeze the ring boundary
        for (int i = 0; i < ringSize; i++) {
            int[] cell = q.poll();
            int r = cell[0], c = cell[1];
            if (grid[r][c] == 9) return steps; // 9 = target, found it

            for (int[] d : dirs) {
                int nr = r + d[0], nc = c + d[1];
                if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
                        && !seen[nr][nc] && grid[nr][nc] != 1) { // 1 = wall
                    seen[nr][nc] = true;
                    q.offer(new int[]{nr, nc});
                }
            }
        }
        steps++;                          // one full ring done = one more step
    }
    return -1;                        // unreachable
}

⚠️ Common Pitfalls

Marking visited on pop instead of enqueue: a node can be enqueued many times before it's popped → exponential blowup and wrong distances. Always mark when you add to the queue.
Using a LinkedList/ArrayList as a queue with remove(0): that's O(n). Use ArrayDeque.
Forgetting the ringSize snapshot: reading q.size() inside the loop mixes rings and destroys your step count.

🎭 Problem Variances — how Big Tech disguises BFS

The tell is "shortest / fewest / minimum steps" in an unweighted setting, or "spreading outward." Common disguises:

Disguise / phrasingWhat's really askedTwist to handle
"Minimum moves / steps / turns to reach X"Shortest path, unweightedLevel-counting BFS; state may be more than (r,c)
"Rotting oranges / spreading infection / fire"Multi-source BFSSeed queue with ALL sources at distance 0
"Word ladder / gene mutation"Implicit graph, BFS shortestNeighbor = one-change; build edges on the fly
"Shortest path with keys / state" (e.g. LC 864)BFS over augmented stateNode = (cell, bitmask); visited keyed on full state
"Nearest 0 / nearest exit / walls-and-gates"Multi-source distance fieldBFS from all targets simultaneously
"Knight's minimum moves on a board"BFS on implicit move graph8 knight offsets as edges; possibly infinite board → bound it
"Bidirectional / meet in the middle"BFS from both endsAlternate frontiers; stop when they intersect
Key distinction: weighted edges break BFS's shortest-path guarantee → that's Dijkstra (Day 7) or 0-1 BFS (Day 6). If the "state" is richer than position, encode it into the node.

🎯 Problems

LC 1926 Nearest Exit from Entrance in Maze Textbook single-source grid BFS with level counting. Med
LC 994 Rotting Oranges Multi-source BFS — seed the queue with every rotten orange at once. Med
LC 127 Word Ladder Words are nodes; edges = one-letter differences. BFS = shortest transformation. Hard