01 Matrix
For a grid of 0s and 1s, return the distance from each cell to its nearest 0.
Open on LeetCode ↗Intuition
The obvious plan is to run a BFS from every 1 out to the nearest 0, but that repeats work across cells and costs O(cells^2). Flip the direction: push every 0 into the queue at once, at distance 0, and BFS outward from all of them simultaneously. Because BFS explores in increasing distance order, the very first time the combined wave reaches a cell is guaranteed to be via the shortest possible path to any 0. One sweep fills in every distance, and no cell is ever revisited once set.
Multi-source BFS from every zero simultaneously. Because all sources start at distance 0, the wave reaches each cell by its shortest route automatically — no per-cell comparison needed, and dist doubles as the visited marker.
Approach
Seed with every 0
Scan the grid once. Every cell holding 0 gets distance 0 and is pushed into the BFS queue immediately, rather than starting one BFS per 1.
Multi-source BFS
Process the queue level by level. For each popped cell, look at its 4 neighbors; any neighbor that has not been assigned a distance yet gets current distance + 1 and is pushed.
Why first-touch is shortest
BFS visits cells in nondecreasing distance order from the combined source set. The first time any cell is reached, that path length is minimal, so no cell needs a second visit or a distance update.
Solution & live demo
Common pitfalls
Running BFS from each 1
for each cell with 1: bfs to nearest 0
for each cell with 0: q.append((r, c))
That's a separate search per cell — O((RC)²) in the worst case. Seeding every zero at distance 0 computes all answers in one pass over the grid.
Using a separate visited array
visited = [[False] * C for _ in range(R)]
dist[nr][nc] is None
None in the distance grid already means unvisited, so a second structure is redundant bookkeeping that can drift out of sync. The first write is also the visit mark.
Writing the distance when dequeuing
r, c = q.popleft() dist[r][c] = ...
dist[nr][nc] = dist[r][c] + 1 q.append((nr, nc))
A cell reachable from two sources gets enqueued twice before either is processed, so the second entry overwrites with a larger distance. Marking at enqueue time admits each cell exactly once, at its true minimum.
Edge cases
Every cell is a source with distance 0; nothing is enqueued.
It is reached in the very first layer, distance 1.
Either [[0]] (distance 0) since at least one 0 is guaranteed to exist.
Distances grow with BFS depth from the nearest border of 0s, still one pass.