86.Walls and Gates
Problem
You are given an m x n grid rooms where each cell holds one of three values:
-1— a wall or obstacle,0— a gate,INF = 2147483647(Integer.MAX_VALUE) — an empty room.
Fill each empty room with the distance to its nearest gate, moving one step at a time up, down, left or right and never through walls. If a room cannot reach any gate, leave it as INF. Modify rooms in place; the method returns nothing.
Examples
Input: rooms = [
[INF, -1, 0, INF],
[INF, INF, INF, -1],
[INF, -1, INF, -1],
[ 0, -1, INF, INF]
]
Output: [
[3, -1, 0, 1],
[2, 2, 1, -1],
[1, -1, 2, -1],
[0, -1, 3, 4]
]
Explanation: e.g. (0,0) is 3 steps from the gate at (3,0); (3,3) is 4 steps from the gate at (0,2).Input: rooms = [[-1]]
Output: [[-1]]
Explanation: There are no empty rooms to fill.Input: rooms = [
[INF, -1],
[ -1, 0]
]
Output: [
[INF, -1],
[ -1, 0]
]
Explanation: The room at (0,0) is walled off from the only gate, so it stays INF.Constraints
m == rooms.length,n == rooms[i].length1 <= m, n <= 250rooms[i][j]is-1,0or2^31 - 1
💡 Key Insight
The distance from a room to its nearest gate is a shortest path in an unweighted grid, which BFS computes. Running BFS per room or per gate repeats work. Instead, put all gates into one queue at distance 0 and run a single multi-source BFS. BFS reaches cells in increasing order of distance, so the first time any gate's wave reaches an empty room, that is already the nearest-gate distance — the room can be filled and never touched again. Every cell is processed once: O(m·n).
Brute Force (BFS from Each Room)
TimeO((m·n)²)SpaceO(m·n)Intuition
Answer the question directly for every empty room: BFS outward from it until the first gate is found. BFS guarantees the first gate found is the closest one. Every room repeats a search over much of the grid.
Algorithm
- For each cell with value
INF:- BFS from it with a fresh
visitedarray, tracking steps. - When a popped cell is a gate (
0), write its step count into the room and stop. - If the BFS ends without a gate, leave the room as
INF.
- BFS from it with a fresh
Visualization
Room (0,0), BFS rings:
step 1: (1,0) (0,1) is a wall
step 2: (2,0), (1,1)
step 3: (3,0) = gate → rooms[0][0] = 3Code
import java.util.ArrayDeque;
import java.util.Deque;
class Solution {
private static final int INF = Integer.MAX_VALUE;
private static final int[][] DIRS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public void wallsAndGates(int[][] rooms) {
for (int r = 0; r < rooms.length; r++) {
for (int c = 0; c < rooms[0].length; c++) {
if (rooms[r][c] == INF) {
rooms[r][c] = nearestGate(rooms, r, c);
}
}
}
}
// Plain BFS from one room; returns steps to the first gate (or INF)
private int nearestGate(int[][] rooms, int sr, int sc) {
int rows = rooms.length, cols = rooms[0].length;
boolean[][] visited = new boolean[rows][cols];
Deque<int[]> queue = new ArrayDeque<>();
queue.offer(new int[]{sr, sc, 0});
visited[sr][sc] = true;
while (!queue.isEmpty()) {
int[] cur = queue.poll();
if (rooms[cur[0]][cur[1]] == 0) {
return cur[2]; // first gate reached is the nearest
}
for (int[] d : DIRS) {
int nr = cur[0] + d[0], nc = cur[1] + d[1];
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
&& !visited[nr][nc] && rooms[nr][nc] != -1) {
visited[nr][nc] = true;
queue.offer(new int[]{nr, nc, cur[2] + 1});
}
}
}
return INF;
}
}Complexity
- Time: O((m·n)²) — up to
m·nrooms, each BFS may scan the whole grid. - Space: O(m·n) —
visitedand the queue of one BFS.
BFS from Each Gate
TimeO(k·m·n)SpaceO(m·n)Intuition
Flip the direction: there are usually far fewer gates than rooms. From each gate, BFS outward and write dist into every room where it improves on the value already stored. A cell whose stored value is already <= the new distance was reached as fast or faster by an earlier gate, so the BFS does not continue through it. After all gates have run, each room holds the minimum over all gates.
Algorithm
- For each gate
(gr, gc), BFS starting at distance0. - For a popped cell at distance
d, look at each in-bounds neighbour:- If
rooms[nr][nc] > d + 1(walls-1and gates0never qualify), set it tod + 1and enqueue it.
- If
- Done — rooms that no gate reached remain
INF.
Visualization
Gate (0,2) first: then gate (3,0):
4 -1 0 1 3 -1 0 1 (0,0): 4 → 3
3 2 1 -1 2 2 1 -1 (1,0): 3 → 2
4 -1 2 -1 1 -1 2 -1 (2,0): 4 → 1
0 -1 3 4 0 -1 3 4 (1,1) stays 2, BFS stops thereCode
import java.util.ArrayDeque;
import java.util.Deque;
class Solution {
private static final int[][] DIRS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public void wallsAndGates(int[][] rooms) {
for (int r = 0; r < rooms.length; r++) {
for (int c = 0; c < rooms[0].length; c++) {
if (rooms[r][c] == 0) bfsFromGate(rooms, r, c);
}
}
}
private void bfsFromGate(int[][] rooms, int gr, int gc) {
int rows = rooms.length, cols = rooms[0].length;
Deque<int[]> queue = new ArrayDeque<>();
queue.offer(new int[]{gr, gc});
while (!queue.isEmpty()) {
int[] cell = queue.poll();
int next = rooms[cell[0]][cell[1]] + 1;
for (int[] d : DIRS) {
int nr = cell[0] + d[0], nc = cell[1] + d[1];
// Only continue where this gate gives a strictly shorter distance
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && rooms[nr][nc] > next) {
rooms[nr][nc] = next;
queue.offer(new int[]{nr, nc});
}
}
}
}
}Complexity
- Time: O(k·m·n) — each of the
kgates may sweep the entire grid. - Space: O(m·n) — the queue.
Multi-Source BFSOptimal
TimeO(m·n)SpaceO(m·n)Intuition
Start the BFS from every gate simultaneously by enqueuing all of them first. The queue then expands in rings of distance 1, 2, 3, ... measured from the nearest gate. The first time a room is reached it receives its final answer, so we only move into cells that are still INF — that check doubles as the visited test, and each cell is processed exactly once.
Algorithm
- Enqueue every cell with value
0. - While the queue is non-empty, pop
(r, c). For each in-bounds neighbour with valueINF:- Set it to
rooms[r][c] + 1and enqueue it.
- Set it to
- Walls are never
INF, so they are skipped automatically; unreachable rooms remainINF.
Dry Run
On the first example (gates at (0,2) and (3,0)):
flowchart TD
L0["distance 0<br/>queue: (0,2), (3,0)"] --> L1["distance 1<br/>(0,3), (1,2), (2,0)"]
L1 --> L2["distance 2<br/>(1,1), (2,2), (1,0)"]
L2 --> L3["distance 3<br/>(3,2), (0,0)"]
L3 --> L4["distance 4<br/>(3,3)"]
L4 --> D["queue empty<br/>all reachable rooms filled"]Code
import java.util.ArrayDeque;
import java.util.Deque;
class Solution {
private static final int INF = Integer.MAX_VALUE;
private static final int[][] DIRS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public void wallsAndGates(int[][] rooms) {
if (rooms.length == 0) return;
int rows = rooms.length, cols = rooms[0].length;
Deque<int[]> queue = new ArrayDeque<>();
// Every gate is a BFS source at distance 0
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (rooms[r][c] == 0) queue.offer(new int[]{r, c});
}
}
while (!queue.isEmpty()) {
int[] cell = queue.poll();
int r = cell[0], c = cell[1];
for (int[] d : DIRS) {
int nr = r + d[0], nc = c + d[1];
// Only unvisited empty rooms; first arrival is the shortest distance
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && rooms[nr][nc] == INF) {
rooms[nr][nc] = rooms[r][c] + 1;
queue.offer(new int[]{nr, nc});
}
}
}
}
}Complexity
- Time: O(m·n) — every cell is enqueued at most once.
- Space: O(m·n) — the queue in the worst case (e.g. a grid full of gates).
Complexity Comparison
| Approach | Time | Space |
|---|---|---|
| BFS from Each Room | O((m·n)²) | O(m·n) |
| BFS from Each Gate | O(k·m·n) | O(m·n) |
| Multi-Source BFS | O(m·n) | O(m·n) |
Common Pitfalls
- Using DFS from each gate without a "strictly better" check — it can revisit cells an exponential number of times or write a non-shortest distance.
- Writing the distance when a cell is popped instead of when it is pushed; the same room is then enqueued several times.
- Computing
INF + 1— adding toInteger.MAX_VALUEoverflows to a negative number. Only add 1 to cells that already hold a real distance. - Moving into gates or walls: only cells equal to
INF(multi-source) or larger than the new distance (per gate) should be updated. - Expecting unreachable rooms to become
-1; they must stayINF.