Breadth-first search
In one line
BFS explores a graph in rings of increasing distance using a queue, so the first time it reaches a node is along a shortest path when all edges cost the same.
What it is
Breadth-first search starts at a source node, visits all its neighbours, then all their unvisited neighbours, and so on. It uses a first-in, first-out queue, so every node at distance 1 is processed before any node at distance 2.
That ordering gives BFS its key property: in an unweighted graph (or one where every edge has the same cost) the distance at which BFS first discovers a node is the length of the shortest path to it. This makes BFS the default tool for minimum number of steps, moves or hops.
BFS visits each vertex and edge at most once, so it runs in O(V + E) time with O(V) space for the queue and the visited set. On a grid of R by C cells, that is O(R x C).
Why interviewers ask
Shortest path in a maze, minimum moves in a puzzle, word ladder, rotting oranges, nearest exit: these are all BFS in disguise. Interviewers check that you reach for BFS rather than DFS when the question says shortest or minimum, mark nodes visited at the right moment, and know the multi-source trick.
How it works
- 1Put the source in a queue with distance 0 and mark it visited.
- 2Pop the front node. If it is the target, its distance is the answer.
- 3For each neighbour that is not visited, mark it visited immediately, set its distance to the current distance + 1, and push it.
- 4Repeat until the queue is empty. Nodes never reached are unreachable.
- 5To process level by level (for example to count minutes or layers), read the queue length at the start of each round and pop exactly that many nodes.
- 6For multi-source BFS, push all starting nodes with distance 0 at the beginning; the expansion then gives each node its distance to the nearest source.
Worked example · Shortest path from 0 to 3
- Undirected graph: 0-1, 0-2, 1-2, 2-3. Adjacency: 0: [1, 2]; 1: [0, 2]; 2: [0, 1, 3]; 3: [2].
- Start: queue [0], visited {0}, dist[0] = 0.
- Pop 0. Neighbours 1 and 2 are new: dist 1 each. Queue [1, 2], visited {0, 1, 2}.
- Pop 1. Neighbours 0 and 2 are already visited, so nothing is added. Queue [2].
- Pop 2. Neighbour 3 is new: dist[3] = dist[2] + 1 = 2. Queue [3].
- Pop 3: it is the target. Shortest path length is 2 (0 to 2 to 3). Following parent pointers 3 to 2 to 0 rebuilds the path.
Picture it
BFS is a ripple from a stone dropped in a pond: the wave reaches everything 1 metre away before anything 2 metres away. Whatever the ripple touches first, it reached by the shortest route.
Reference code
from collections import deque
def shortest_path(grid, start, target):
# grid cells: 0 = open, 1 = wall; start and target are (row, col)
rows, cols = len(grid), len(grid[0])
q = deque([(start, 0)])
seen = {start}
while q:
(r, c), d = q.popleft()
if (r, c) == target:
return d
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if not (0 <= nr < rows and 0 <= nc < cols):
continue
if grid[nr][nc] == 1 or (nr, nc) in seen:
continue
seen.add((nr, nc)) # mark when pushed, not when popped
q.append(((nr, nc), d + 1))
return -1 # target unreachableEach cell enters the queue at most once because it is marked as seen when pushed. The first time the target is popped, d is the fewest moves. Time and space are O(rows x cols).
Key patterns
Shortest path in unweighted graphs
When: Minimum steps, moves, hops or transformations
How: Plain BFS from the source; return the distance when the target is popped or first discovered.
Multi-source BFS
When: Distance to the nearest of many sources: rotting oranges, walls and gates, 01 matrix
How: Push all sources with distance 0 before starting; one pass gives every cell its nearest-source distance.
Level-order rounds
When: Count rounds, minutes or layers
How: Snapshot len(queue) at the start of each round and increase the round counter once per round.
BFS over states
When: Word ladder, open the lock, sliding puzzle
How: A node is a whole state (a string or a tuple); generate neighbours by legal one-step changes and keep a visited set of states.
0-1 BFS
When: Edges cost only 0 or 1
How: Use a deque: push 0-cost neighbours to the front and 1-cost neighbours to the back. Still O(V + E).
Real-world use
Degrees of connection
LinkedIn shows whether someone is a 1st, 2nd or 3rd degree connection: that is the BFS distance from you in the connection graph, typically searched from both ends to keep it fast.
Web crawlers
Search engine crawlers commonly take a breadth-first approach from seed pages so they cover many sites at shallow depth before going deep into any one site, using a URL frontier queue.
Garbage collection
Cheney's copying garbage collector, the basis of many young-generation collectors, copies reachable objects in BFS order using the to-space itself as the queue.
Games and robotics
Tile-based games and warehouse robots on uniform grids use BFS (or its weighted cousins) to find the fewest moves to a target and to compute distance maps from many targets at once.
In a project
A food delivery app wants to show, for every block in a city grid, the walking distance in blocks to the nearest pickup locker, and to update it nightly.
- Model the city as a grid: open cells are walkable, buildings are walls.
- Run one multi-source BFS: push every locker cell with distance 0 into the queue at the start.
- Expand normally; each cell gets its distance the first time it is reached, which is the distance to its nearest locker.
- Store the resulting distance map and look it up per order in O(1).
- Flag cells with no distance as unreachable and send them to manual review.
Impact: One O(R x C) pass replaces a separate search per locker (O(lockers x R x C)), so a 2000 by 2000 grid with 500 lockers is computed in seconds instead of hours.
Trade-offs
Use it when
- Shortest path or minimum steps with equal edge costs
- Anything layered: levels of a tree, rounds of spreading, degrees of separation
- You want the nearest solution first and the search space is broad but the answer is shallow
Avoid it when
- Edges have different non-negative weights: use Dijkstra
- You only need to know if a path exists or to explore everything: DFS is equally good and often simpler
- The graph is very wide and memory is tight: the frontier can hold a huge number of nodes; consider bidirectional BFS or iterative deepening
Cost: O(V + E) time and O(V) space. On a grid: O(R x C) for both.
Common mistakes
- Marking nodes visited when popped instead of when pushed, so the same node is enqueued many times and the search slows down badly.
- Using a Python list with pop(0), which is O(n) per pop; use collections.deque.
- Using DFS for a shortest path question: DFS finds a path, not the shortest one.
- Running a separate BFS from every source when one multi-source BFS would do.
- Forgetting to handle the case where the start equals the target or the start is a wall.
Interview questions
Quick revision
- Queue (deque), visited set, distance per node.
- Mark visited when pushing, not when popping.
- Unweighted shortest path = BFS. Weighted = Dijkstra.
- Rounds: snapshot len(queue) each level.
- Multi-source: push all sources at distance 0 first.
- O(V + E) time, O(V) space; grid O(R x C).
- 0-1 weights: deque, 0-cost to front, 1-cost to back.
Check yourself
1. Why should a node be marked visited when it is pushed onto the queue rather than when it is popped?
2. Which problem is best solved by multi-source BFS?
3. What is the time complexity of BFS on a graph with V vertices and E edges, using an adjacency list?
4. Edges have weights 1, 5 and 10. Does plain BFS find the cheapest path?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.