You are given an m x n grid rooms initialized with these three possible values.
-1 A wall or an obstacle.
0 A gate.
INF Infinity means an empty room. We use the value 231 - 1 = 2147483647 to represent INF as you may assume that the distance to a gate is less than 2147483647.
Fill each empty room with the distance to its nearest gate. If it is impossible to reach a gate, it should be filled with INF.
It is a classic backtracking problem. We can start from each gate (0 point) and search for its neighbors. We can use either DFS or BFS for the solution.
We first find all the positions of the gates (val=0), and then backtrack to all possible empty spaces (val=INF), updating the value of the empty space cells to the minimum distance to the nearest gate.
This can be done using a simple BFS, updating the paths similarly to Dijkstra’s shortest path problem, keeping the invariant room[i][j] <= room[r][c]+1, where room[i][j] is a neighbor empty space and room[r][c] is the current position of a node on a path to escape. It is important to know where the walls are and prune the search tree from the position where a wall is discovered (val=-1).
The key idea is to treat each gate (cell with value 0) as a starting point and use DFS to propagate the minimum distance to all reachable empty rooms (INF). We only update a room if the new distance is less than its current value, ensuring the shortest path from any gate is recorded.
If out of bounds, or at a wall (-1), or the current distance is not better, return. We do not need an explicit visited set in this DFS approach. This is because we only recurse into a cell if the new distance is less than its current value. Once a cell has been updated with the shortest distance from any gate, future DFS calls with a greater or equal distance will not proceed further, effectively preventing cycles and redundant work.
Update the room with the current distance.
Recursively call DFS for all four directions (up, down, left, right) with distance + 1.
classSolution {
public:void wallsAndGates(vector<vector<int>>& rooms) {
int m = rooms.size(), n = rooms[0].size();
for (int i =0; i < m; ++i) {
for (int j =0; j < n; ++j) {
if (rooms[i][j] ==0) dfs(rooms, i, j, 0);
}
}
}
voiddfs(vector<vector<int>>& rooms, int i, int j, int dist) {
int m = rooms.size(), n = rooms[0].size();
if (i <0|| i >= m || j <0|| j >= n || rooms[i][j] < dist) return;
rooms[i][j] = dist;
dfs(rooms, i+1, j, dist+1);
dfs(rooms, i-1, j, dist+1);
dfs(rooms, i, j+1, dist+1);
dfs(rooms, i, j-1, dist+1);
}
};
classSolution {
publicvoidwallsAndGates(int[][] rooms) {
int m = rooms.length, n = rooms[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (rooms[i][j]== 0) {
dfs(rooms, i, j, 0);
}
}
}
}
privatevoiddfs(int[][] rooms, int i, int j, int dist) {
int m = rooms.length, n = rooms[0].length;
if (i < 0 || i >= m || j < 0 || j >= n || rooms[i][j]< dist) return;
rooms[i][j]= dist;
dfs(rooms, i+1, j, dist+1);
dfs(rooms, i-1, j, dist+1);
dfs(rooms, i, j+1, dist+1);
dfs(rooms, i, j-1, dist+1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
classSolution {
funwallsAndGates(rooms: Array<IntArray>) {
val m = rooms.size
val n = rooms[0].size
fundfs(i: Int, j: Int, d: Int) {
if (i !in0 until m || j !in0 until n || rooms[i][j] < d) return rooms[i][j] = d
dfs(i+1, j, d+1)
dfs(i-1, j, d+1)
dfs(i, j+1, d+1)
dfs(i, j-1, d+1)
}
for (i in0 until m) for (j in0 until n) if (rooms[i][j] ==0) dfs(i, j, 0)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
classSolution:
defwallsAndGates(self, rooms: list[list[int]]) ->None:
m, n = len(rooms), len(rooms[0])
defdfs(i: int, j: int, d: int):
if i <0or i >= m or j <0or j >= n or rooms[i][j] < d:
return rooms[i][j] = d
dfs(i+1, j, d+1)
dfs(i-1, j, d+1)
dfs(i, j+1, d+1)
dfs(i, j-1, d+1)
for i in range(m):
for j in range(n):
if rooms[i][j] ==0:
dfs(i, j, 0)
impl Solution {
pubfnwalls_and_gates(rooms: &mut Vec<Vec<i32>>) {
let m = rooms.len();
let n = rooms[0].len();
fndfs(rooms: &mut Vec<Vec<i32>>, i: i32, j: i32, d: i32, m: i32, n: i32) {
if i <0|| i >= m || j <0|| j >= n || rooms[i asusize][j asusize] < d { return; }
rooms[i asusize][j asusize] = d;
dfs(rooms, i+1, j, d+1, m, n);
dfs(rooms, i-1, j, d+1, m, n);
dfs(rooms, i, j+1, d+1, m, n);
dfs(rooms, i, j-1, d+1, m, n);
}
for i in0..m {
for j in0..n {
if rooms[i][j] ==0 {
dfs(rooms, i asi32, j asi32, 0, m asi32, n asi32);
}
}
}
}
}
⏰ Time complexity: O(mn) — Each cell is visited at most once for each gate, but since we only update if the new distance is smaller, the total work is proportional to the number of cells.
🧺 Space complexity: O(mn) — For recursion stack in the worst case (all empty rooms).
Instead of running a search from every empty room, we can treat all gates (0 cells) as sources and perform a breadth-first search (BFS) simultaneously from all of them. This way, each empty room is filled with the shortest distance to any gate, and we avoid redundant work.
Initialize a queue and enqueue all gate positions (rooms[i][j] == 0).
For each position in the queue, pop it and check its four neighbors (up, down, left, right).
If a neighbor is an empty room (INF), update its value to the current cell’s value + 1 and enqueue it.
Repeat until the queue is empty.
This approach ensures that each room is updated with the shortest distance to a gate, as BFS explores all nodes at distance d before nodes at distance d+1.
classSolution {
publicvoidwallsAndGates(int[][] rooms) {
int m = rooms.length, n = rooms[0].length;
Queue<int[]> q =new LinkedList<>();
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (rooms[i][j]== 0) q.offer(newint[]{i, j});
int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
while (!q.isEmpty()) {
int[] cell = q.poll();
int i = cell[0], j = cell[1];
for (int[] d : dirs) {
int ni = i + d[0], nj = j + d[1];
if (ni < 0 || ni >= m || nj < 0 || nj >= n || rooms[ni][nj]!= Integer.MAX_VALUE) continue;
rooms[ni][nj]= rooms[i][j]+ 1;
q.offer(newint[]{ni, nj});
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classSolution {
funwallsAndGates(rooms: Array<IntArray>) {
val m = rooms.size
val n = rooms[0].size
val q = ArrayDeque<Pair<Int, Int>>()
for (i in0 until m) for (j in0 until n) if (rooms[i][j] ==0) q.add(i to j)
val dirs = listOf(1 to 0, -1 to 0, 0 to 1, 0 to -1)
while (q.isNotEmpty()) {
val(i, j) = q.removeFirst()
for ((di, dj) in dirs) {
val ni = i + di; val nj = j + dj
if (ni !in0 until m || nj !in0 until n || rooms[ni][nj] !=Int.MAX_VALUE) continue rooms[ni][nj] = rooms[i][j] + 1 q.add(ni to nj)
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
classSolution:
defwallsAndGates(self, rooms: list[list[int]]) ->None:
from collections import deque
m, n = len(rooms), len(rooms[0])
q = deque()
for i in range(m):
for j in range(n):
if rooms[i][j] ==0:
q.append((i, j))
dirs = [(1,0),(-1,0),(0,1),(0,-1)]
while q:
i, j = q.popleft()
for di, dj in dirs:
ni, nj = i+di, j+dj
if0<= ni < m and0<= nj < n and rooms[ni][nj] ==2147483647:
rooms[ni][nj] = rooms[i][j] +1 q.append((ni, nj))
use std::collections::VecDeque;
impl Solution {
pubfnwalls_and_gates(rooms: &mut Vec<Vec<i32>>) {
let m = rooms.len();
let n = rooms[0].len();
letmut q = VecDeque::new();
for i in0..m {
for j in0..n {
if rooms[i][j] ==0 {
q.push_back((i, j));
}
}
}
let dirs = [(1,0),(-1,0),(0,1),(0,-1)];
whilelet Some((i, j)) = q.pop_front() {
for (di, dj) in dirs.iter() {
let ni = i asi32+ di;
let nj = j asi32+ dj;
if ni <0|| ni >= m asi32|| nj <0|| nj >= n asi32 { continue; }
let (ni, nj) = (ni asusize, nj asusize);
if rooms[ni][nj] ==i32::MAX {
rooms[ni][nj] = rooms[i][j] +1;
q.push_back((ni, nj));
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
classSolution {
wallsAndGates(rooms: number[][]):void {
constm=rooms.length, n=rooms[0].length;
constq: [number, number][] = [];
for (leti=0; i<m; i++)
for (letj=0; j<n; j++)
if (rooms[i][j] ===0) q.push([i, j]);
constdirs= [[1,0],[-1,0],[0,1],[0,-1]];
while (q.length) {
const [i, j] =q.shift()!;
for (const [di, dj] ofdirs) {
constni=i+di, nj=j+dj;
if (ni<0||ni>=m||nj<0||nj>=n||rooms[ni][nj] !==2147483647) continue;
rooms[ni][nj] =rooms[i][j] +1;
q.push([ni, nj]);
}
}
}
}