For each cell, find the distance to the nearest 0. Running BFS from every 1 would be quadratic — instead flip it: seed one BFS with every 0 at once and let the wave wash outward, so the first time a cell is reached is its shortest distance.
Given an m × n binary matrix of 0s and 1s, replace each cell with its 4-directional distance to the nearest 0. Every 0 maps to 0.
Worked example. Input:
[[0,0,0], [0,1,0], [1,1,1]]
Output:
[[0,0,0], [0,1,0], [1,2,1]]
The center 1 at (1,1) is one step from three different 0s, so it becomes 1. The cell (2,1) is two steps from the nearest 0, so it becomes 2.
1, BFS until you hit a 0” — but that re-explores the grid for every cell. Flip it: enqueue every 0 at distance 0 and run a single multi-source BFS outward. Because BFS expands in concentric layers, the first time the wave touches a cell is necessarily along a shortest path — so the distance you assign on first contact is final.0-cell into the queue (its answer is already 0); set every 1-cell to Infinityto mark it unvisited. This collection of zeros is BFS “layer 0” — all sources at once.dist[cell] + 1, this BFS wave reaches it sooner: set dist[neighbor] = dist[cell] + 1 and enqueue it.dist grid.1s with the distance you compute and then use that same grid to decide “visited.” Mixing the input markers with the output distances corrupts the relaxation check. Keep a separate dist grid initialized to Infinity for the 1s; the dist[nbr] > dist[cur] + 1 test then doubles as the visited test for free.There is also an elegant dynamic-programming solution in O(mn) time and O(1) extra space (besides the output). Sweep top-left → bottom-right taking the min of the up and left neighbors + 1, then sweep bottom-right → top-left taking the min of the down and right neighbors + 1. Two passes suffice because any shortest path to a 0 is monotone in the four diagonal directions covered by the two sweeps. See the Approaches tab.
3×3. Every 0 is its own nearest zero (distance 0); we seed the BFS queue with all of them at once and set every 1 to ∞ (unvisited).1function updateMatrix(mat: number[][]): number[][] {2▶ const rows = mat.length;3▶ const cols = mat[0].length;4▶ const dist: number[][] = mat.map((row) => row.slice());5▶ const queue: [number, number][] = [];67 // Seed: every 0 is distance 0; every 1 starts at Infinity (unvisited)8 for (let r = 0; r < rows; r++) {9 for (let c = 0; c < cols; c++) {10 if (mat[r][c] === 0) queue.push([r, c]);11 else dist[r][c] = Infinity;12 }13 }1415 const dirs: [number, number][] = [[-1,0],[1,0],[0,-1],[0,1]];16 let head = 0;17 while (head < queue.length) {18 const [r, c] = queue[head++];19 for (const [dr, dc] of dirs) {20 const nr = r + dr, nc = c + dc;21 if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue;22 if (dist[nr][nc] <= dist[r][c] + 1) continue; // already shorter23 dist[nr][nc] = dist[r][c] + 1; // first reach = shortest24 queue.push([nr, nc]);25 }26 }2728 return dist;29}
function updateMatrix(mat: number[][]): number[][] {
const rows = mat.length;
const cols = mat[0].length;
const dist: number[][] = mat.map((row) => row.slice());
const queue: [number, number][] = [];
// Seed: every 0 is distance 0; every 1 starts at Infinity (unvisited)
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (mat[r][c] === 0) queue.push([r, c]);
else dist[r][c] = Infinity;
}
}
const dirs: [number, number][] = [[-1,0],[1,0],[0,-1],[0,1]];
let head = 0;
while (head < queue.length) {
const [r, c] = queue[head++];
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue;
if (dist[nr][nc] <= dist[r][c] + 1) continue; // already shorter
dist[nr][nc] = dist[r][c] + 1; // first reach = shortest
queue.push([nr, nc]);
}
}
return dist;
}dist matrix so we never tangle the input markers with the distances we are writing. Zeros keep their value; ones will be overwritten.0-cell (its distance is already 0) and sets every 1-cell to Infinity. Seeding all zeros before the first pop is what makes this a single linear BFS instead of many.head advances through the shared array; this is an O(1) dequeue and avoids the O(n) cost of Array.shift().dist[nr][nc] <= dist[r][c] + 1 skips cells already reached as soon (or sooner). Otherwise we set dist[nr][nc] = dist[r][c] + 1 and enqueue it. Because BFS visits in non-decreasing distance order, this first assignment is the minimum.head catches up to queue.length, every reachable cell (all of them, since the grid is connected through non-walls) holds its shortest distance. Return dist.dist grid is the output, so it is not counted as extra.dirs from 4 to 8 entries — the BFS structure is unchanged.Infinity (LeetCode guarantees at least one 0, but state the edge case).function updateMatrix(mat: number[][]): number[][] {
const rows = mat.length;
const cols = mat[0].length;
const dist: number[][] = mat.map((row) => row.slice());
const queue: [number, number][] = [];
// Seed: every 0 is distance 0; every 1 starts at Infinity (unvisited)
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (mat[r][c] === 0) queue.push([r, c]);
else dist[r][c] = Infinity;
}
}
const dirs: [number, number][] = [[-1,0],[1,0],[0,-1],[0,1]];
let head = 0;
while (head < queue.length) {
const [r, c] = queue[head++];
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue;
if (dist[nr][nc] <= dist[r][c] + 1) continue; // already shorter
dist[nr][nc] = dist[r][c] + 1; // first reach = shortest
queue.push([nr, nc]);
}
}
return dist;
}dist grid is the output, so it is not counted as extra.The shortest path to a 0 either comes from above/left or from below/right. Two directional sweeps capture both, so no queue is needed and the answer is built in place.
function updateMatrix(mat: number[][]): number[][] {
const rows = mat.length;
const cols = mat[0].length;
const INF = rows + cols; // larger than any real distance
const dist: number[][] = mat.map((row) =>
row.map((v) => (v === 0 ? 0 : INF)),
);
// Pass 1: top-left -> bottom-right (look up and left)
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (r > 0) dist[r][c] = Math.min(dist[r][c], dist[r - 1][c] + 1);
if (c > 0) dist[r][c] = Math.min(dist[r][c], dist[r][c - 1] + 1);
}
}
// Pass 2: bottom-right -> top-left (look down and right)
for (let r = rows - 1; r >= 0; r--) {
for (let c = cols - 1; c >= 0; c--) {
if (r < rows - 1) dist[r][c] = Math.min(dist[r][c], dist[r + 1][c] + 1);
if (c < cols - 1) dist[r][c] = Math.min(dist[r][c], dist[r][c + 1] + 1);
}
}
return dist;
}0 is monotone, so the up/left sweep handles paths coming from those directions and the down/right sweep handles the rest. Use INF = rows + cols (not real Infinity) so + 1 never overflows.The direct reading of the problem: for each 1, BFS outward until you hit a 0. Correct but quadratic.
function updateMatrix(mat: number[][]): number[][] {
const rows = mat.length;
const cols = mat[0].length;
const dirs: [number, number][] = [[-1,0],[1,0],[0,-1],[0,1]];
const res: number[][] = mat.map((row) => row.slice());
for (let sr = 0; sr < rows; sr++) {
for (let sc = 0; sc < cols; sc++) {
if (mat[sr][sc] === 0) continue;
// BFS from this single 1 to the nearest 0
const seen = new Set<string>([`${sr},${sc}`]);
let queue: [number, number, number][] = [[sr, sc, 0]];
let head = 0;
while (head < queue.length) {
const [r, c, d] = queue[head++];
if (mat[r][c] === 0) { res[sr][sc] = d; break; }
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue;
const key = `${nr},${nc}`;
if (seen.has(key)) continue;
seen.add(key);
queue.push([nr, nc, d + 1]);
}
}
}
}
return res;
}mn ones can scan up to mn cells → O((mn)²). This is the approach the multi-source BFS exists to avoid: invert it and run a single BFS seeded with all the zeros.| distance to nearest target from EVERY cell | multi-source BFS from all targets |
| "nearest 0 / gate / exit" on a grid | seed queue with all sources at distance 0 |
| avoid (mn)² of per-cell BFS | one BFS seeded with all sources |
| O(1) extra space wanted | two-pass DP: up/left then down/right |
const dist = mat.map((row) => row.slice());
const queue: [number, number][] = [];
for (let r = 0; r < rows; r++)
for (let c = 0; c < cols; c++) {
if (mat[r][c] === 0) queue.push([r, c]);
else dist[r][c] = Infinity;
}
let head = 0;
while (head < queue.length) {
const [r, c] = queue[head++];
// for each in-bounds neighbor with dist > dist[r][c] + 1:
// dist[nr][nc] = dist[r][c] + 1; queue.push([nr, nc]);
}
return dist;mat=[[0,0,0],[0,1,0],[1,1,1]], what is the output cell at (2,1)?