Walls and Gates is the classic problem for learning multi-source BFS — running one breadth-first search from many starting points at once. It looks like a maze, but the trick is to flood outward from every gate simultaneously.
Problem. You are given an m x n grid of rooms. Each cell is one of three values: -1 is a
wall, 0 is a gate, and 2147483647 (treated as INF) is an empty room. Fill each empty
room with its distance to the nearest gate. If a room cannot reach any gate, leave it as INF.
Example: a gate at (1,2) and a gate at (2,0), with walls splitting the grid. Every empty room ends
up holding the number of steps to whichever gate is closest.
The slow way first
The obvious idea: for each empty room, run a BFS to find the closest gate. That is correct, but you pay a full grid traversal per empty room — roughly O((m·n)²). On a large board that is hopelessly slow.
The question to ask: instead of searching from each room to a gate, why not search from the gates outward? If I expand from all gates at the same time, the first time a wave touches a room, that wave came from the nearest gate — by definition of breadth-first search.
The idea: flood from every gate at once
Seed a single BFS queue with all the gates. Then expand level by level. Every room reached on the first wave is distance 1, the next wave is distance 2, and so on. Because all gates start together, whichever wavefront reaches a room first is the closest gate — so the first value we write is the correct shortest distance, and we never overwrite it.
The key insight: a room is only filled while it is still INF. The first wave to arrive claims it, so it always records the nearest gate and is never touched again.
Walk through it
Step through the animation. Both gates start the wavefront together. The first ring of empty neighbors gets 1, the next ring gets 2, and the single room tucked beside a gate gets 1 directly. When the queue drains, every reachable room holds its shortest distance.
Pseudocode
queue = empty queue
for every cell:
if cell is a gate (0):
add its coordinates to queue # multi-source seed
while queue is not empty:
(r, c) = pop front of queue
for each of the 4 neighbors (nr, nc):
if neighbor is in bounds AND still INF:
grid[nr][nc] = grid[r][c] + 1 # nearest gate wins
add (nr, nc) to queueThe Python solution
def walls_and_gates(rooms):
INF = 2147483647
R, C = len(rooms), len(rooms[0])
q = deque()
for r in range(R):
for c in range(C):
if rooms[r][c] == 0:
q.append((r, c))
while q:
r, c = q.popleft()
for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)):
nr, nc = r + dr, c + dc
if 0 <= nr < R and 0 <= nc < C and rooms[nr][nc] == INF:
rooms[nr][nc] = rooms[r][c] + 1
q.append((nr, nc))- We seed the queue with every gate first (the nested loop) — that is what makes it multi-source.
q.popleft()keeps the search breadth-first, so rooms are filled in order of increasing distance.- The four
(dr, dc)offsets are up, down, right, left — the grid neighbors. - The guard
rooms[nr][nc] == INFis doing two jobs: it skips walls (-1) and gates (0), and it skips already-filled rooms — so the first (nearest) wave wins and we never overwrite. - We set the distance to the current cell plus one, then enqueue the new room to expand later.
Complexity
| Case | Time | Notes |
|---|---|---|
| BFS from each empty room | O((m·n)²) (moderate) | one traversal per room |
| Multi-source BFS (this solution) | O(m·n) (moderate) | each cell enqueued once |
O(m·n) (moderate)Because each cell enters the queue at most once, the whole flood is linear in the number of cells. The space is the queue, which in the worst case holds a large fraction of the grid.
When this pattern shows up
Whenever a problem asks for the distance to the nearest of many sources — nearest gate, nearest rotten orange, nearest 1 in a binary matrix — reach for multi-source BFS. Seed the queue with all sources at distance 0, then expand. The first wave to reach a cell is always the closest source.
Do not run a separate BFS per gate and take the minimum — that is the slow O((m·n)²) trap. One queue
seeded with all gates gives every nearest-distance in a single linear pass. Also remember to check that
a room is still INF before writing, or you will overwrite a closer distance with a farther one.
Practice
During the BFS, a room is reached at the same moment by waves from two different gates. Which distance gets written?
1. Why do we seed the queue with all gates before starting the loop?
2. What does the check rooms[nr][nc] == INF accomplish?
3. Why is multi-source BFS O(m·n) instead of O((m·n)²)?
4. If a room can never reach any gate, what value does it keep?