🚀 Phases 1–4 are live — Days 1–13 + Day 17 are fully written. See the roadmap →

Breadth-First Search (BFS)

Breadth-first search is the algorithm that explores layer by layer — visit all the neighbours of the start, then all of their unvisited neighbours, and so on. It uses a queue (the Day 4 data structure) instead of a stack.

The single most important property: BFS finds the shortest path (in number of edges) from the start to every node it reaches. That one property is the reason BFS exists.

Step through it

Press Next to watch BFS spread outward from A one layer at a time. Notice how C is visited before D and E — even though DFS visited D and E first on the exact same graph.

BFS from A — visits A, then B and C (distance 1), then D, E, F (distance 2)
ABCDEF
6 nodes · 5 edges
step 0/6

DFS dove deep into one branch first. BFS spreads out radially. Same graph, same start — different exploration order.

The template

The single line of difference from DFS: use a queue (FIFO) instead of a stack (LIFO).

⚠️

Mark visited when you push, not when you pop. A common bug: if you only mark visited at pop time, the same node can be added to the queue multiple times by different predecessors — you’ll process it more than once and your distance counts will be wrong. Always mark on push.

Why BFS finds the shortest path

Each layer of the BFS exploration corresponds to a fixed distance from the start. The start is distance 0. Its neighbours are distance 1. Their unvisited neighbours are distance 2. And so on.

The queue’s FIFO property ensures we always finish a layer before starting the next — and the first time we see any node X, we’ve reached it through the shortest possible path. (If a shorter path existed, we’d have seen X in an earlier layer.)

Computing distances

To get distances (not just visited-ness), store the distance alongside the node:

Level-by-level BFS

Some problems need you to process one entire layer at a time (e.g., “level order traversal of a tree”). The standard trick: snapshot the queue size at the top of each iteration.

def level_order(start, adj):
    visited = {start}
    queue = deque([start])
    levels = []
    while queue:
        size = len(queue)
        layer = []
        for _ in range(size):
            node = queue.popleft()
            layer.append(node)
            for nei in adj[node]:
                if nei not in visited:
                    visited.add(nei)
                    queue.append(nei)
        levels.append(layer)
    return levels

size captures the count of nodes in the current layer. The inner loop processes exactly those nodes, after which the queue holds only the next layer.

Multi-source BFS

Sometimes you want shortest distances from multiple sources simultaneously. Examples: “how far is every cell from the nearest exit?”, or Rotting Oranges where every rotten orange spreads at the same time.

Just start with all the sources in the queue and distance 0:

def multi_source_bfs(sources, adj):
    dist = {s: 0 for s in sources}
    queue = deque(sources)
    while queue:
        node = queue.popleft()
        for nei in adj[node]:
            if nei not in dist:
                dist[nei] = dist[node] + 1
                queue.append(nei)
    return dist

Same template, just multiple starting points. Each cell’s distance is to the nearest source.

What BFS is good at

  • Shortest path on unweighted graphs. The killer use case. Single source, single (or all) destinations.
  • “Levels” or “layers.” Anything you’d phrase as “after k steps.”
  • Detecting bipartiteness. Color the start red, all neighbours blue, all their neighbours red, etc. If two adjacent nodes ever get the same color, it’s not bipartite.
  • Multi-source spread. Rotting Oranges, infection spread, flood fill from multiple seeds.
  • Word ladder / state-space search where each transition has cost 1.

What BFS is bad at

  • Weighted shortest paths. BFS assumes all edges are weight 1. For varying weights, use Dijkstra (Day 21).
  • Counting paths or finding all paths. DFS is more natural — though both work.
  • Deep recursive structure (DAG topological sort, tree backtracking) — DFS reads more naturally.

Complexity

QuantityCost
TimeO(V + E)
Space (queue)O(V)
Space (visited)O(V)

Each node enters and exits the queue at most once. Each edge is examined twice in an undirected graph. Same O(V + E) as DFS — the difference is the order of visits, not the cost.

The pattern to memorize

BFS = “explore everything one step away, then everything two steps away, then three…”

Use a queue. Mark visited on push. Process layer by layer if you care about distance.

Next: how to choose DFS vs BFS for any given problem.