Distance to the nearest zero
A grid holds 0s and 1s, with at least one 0. For every cell, return
the number of steps to the nearest cell holding 0, where a step moves up, down, left or right. Cells that
hold 0 are at distance 0.
Examples
| Input | Output | Why |
|---|---|---|
mat = [[0,0,0],[0,1,0],[1,1,1]] | [[0,0,0],[0,1,0],[1,2,1]] | The bottom-middle cell is two steps from the nearest 0. |
mat = [[1,1,1],[1,1,1],[1,1,0]] | [[4,3,2],[3,2,1],[2,1,0]] | A single 0 in the corner. |
mat = [[0,1,1,1,1]] | [[0,1,2,3,4]] | One row. |
Constraints
- 1 ≤ rows, cols, and rows · cols ≤ 10⁴
- At least one cell is 0
Hints
Hint 1
A breadth-first search from each 1 is correct but costs O((rows · cols)²).
Hint 2
Turn it around: search outwards from the zeros. Put every 0 in the queue at distance 0 before starting.
Hint 3
The first time the search reaches a cell, it is from the nearest zero. Mark it then.
Where people lose marks
- One search per cell: correct but quadratic, and too slow at 10⁴ cells.
- Seeding the queue with one zero and treating the others as ordinary cells.
- Updating a distance after the cell has already been reached.
Dry run on [[1,1,1,1],[1,0,1,1],[1,1,1,0]]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 3
All 2 source cells go into the queue together, at distance 0.
-
Step 2 / 3
Ring 1: 6 cells at distance 1, each one step from the previous ring. A cell is labelled by whichever source reaches it first, which is the nearest.
-
Step 3 / 3
Ring 2: 4 cells at distance 2, each one step from the previous ring. A cell is labelled by whichever source reaches it first, which is the nearest.
How to think about it
Searching from every 1 to find its nearest 0 repeats almost all of the work. Instead run one breadth-first search that starts from all the zeros at once: put every 0 in the queue at distance 0, then expand ring by ring. Because every source starts together, the first time any cell is reached it is reached from its nearest zero, and its distance is final.
This is multi-source breadth-first search, the same idea as "minutes until every orange rots" or "distance from every room to the nearest exit".
Complexity. Time O(rows · cols) · Space O(rows · cols)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
from collections import deque
def nearest_zero(mat):
rows, cols = len(mat), len(mat[0])
dist = [[-1] * cols for _ in range(rows)]
q = deque()
for r in range(rows):
for c in range(cols):
if mat[r][c] == 0:
dist[r][c] = 0 # every zero starts in the queue
q.append((r, c))
while q:
r, c = q.popleft()
for a, b in ((r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)):
if 0 <= a < rows and 0 <= b < cols and dist[a][b] == -1:
dist[a][b] = dist[r][c] + 1 # first reached = from the nearest zero
q.append((a, b))
return dist#include <queue>
#include <utility>
#include <vector>
std::vector<std::vector<int>> solve(const std::vector<std::vector<int>>& mat) {
const int rows = mat.size(), cols = mat[0].size();
std::vector<std::vector<int>> dist(rows, std::vector<int>(cols, -1));
std::queue<std::pair<int, int>> q;
for (int r = 0; r < rows; ++r)
for (int c = 0; c < cols; ++c)
if (mat[r][c] == 0) { dist[r][c] = 0; q.push({r, c}); } // all zeros first
const int dr[] = {1, -1, 0, 0}, dc[] = {0, 0, 1, -1};
while (!q.empty()) {
auto [r, c] = q.front();
q.pop();
for (int k = 0; k < 4; ++k) {
int a = r + dr[k], b = c + dc[k];
if (a >= 0 && b >= 0 && a < rows && b < cols && dist[a][b] == -1) {
dist[a][b] = dist[r][c] + 1;
q.push({a, b});
}
}
}
return dist;
}import java.util.*;
class Solution {
public int[][] nearestZero(int[][] mat) {
int rows = mat.length, cols = mat[0].length;
int[][] dist = new int[rows][cols];
ArrayDeque<int[]> q = new ArrayDeque<>();
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++) {
dist[r][c] = mat[r][c] == 0 ? 0 : -1;
if (mat[r][c] == 0) q.add(new int[]{r, c}); // all zeros first
}
int[] dr = {1, -1, 0, 0}, dc = {0, 0, 1, -1};
while (!q.isEmpty()) {
int[] cell = q.poll();
for (int k = 0; k < 4; k++) {
int a = cell[0] + dr[k], b = cell[1] + dc[k];
if (a >= 0 && b >= 0 && a < rows && b < cols && dist[a][b] == -1) {
dist[a][b] = dist[cell[0]][cell[1]] + 1;
q.add(new int[]{a, b});
}
}
}
return dist;
}
}function nearestZero(mat) {
const rows = mat.length, cols = mat[0].length;
const dist = Array.from({ length: rows }, () => new Array(cols).fill(-1));
const q = [];
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) if (mat[r][c] === 0) { dist[r][c] = 0; q.push([r, c]); }
for (let head = 0; head < q.length; head++) {
const [r, c] = q[head];
for (const [a, b] of [[r + 1, c], [r - 1, c], [r, c + 1], [r, c - 1]]) {
if (a >= 0 && b >= 0 && a < rows && b < cols && dist[a][b] === -1) {
dist[a][b] = dist[r][c] + 1; // reached from the nearest zero
q.push([a, b]);
}
}
}
return dist;
}use std::collections::VecDeque;
fn solve<const C: usize>(mat: &[[i64; C]]) -> Vec<Vec<i64>> {
let rows = mat.len();
let mut dist = vec![vec![-1i64; C]; rows];
let mut q = VecDeque::new();
for r in 0..rows {
for c in 0..C {
if mat[r][c] == 0 {
dist[r][c] = 0; // all zeros first
q.push_back((r, c));
}
}
}
while let Some((r, c)) = q.pop_front() {
let around = [(r + 1, c), (r.wrapping_sub(1), c), (r, c + 1), (r, c.wrapping_sub(1))];
for (a, b) in around {
if a < rows && b < C && dist[a][b] == -1 {
dist[a][b] = dist[r][c] + 1;
q.push_back((a, b));
}
}
}
dist
}import qualified Data.Map.Strict as M
import Data.Sequence (Seq (..), (|>))
import qualified Data.Sequence as Q
solve :: [[Int]] -> [[Int]]
solve mat = [[dist M.! (r, c) | c <- [0 .. cols - 1]] | r <- [0 .. rows - 1]]
where
rows = length mat
cols = length (head mat)
zeros = [(r, c) | (r, row) <- zip [0 ..] mat, (c, 0) <- zip [0 ..] row]
-- every zero starts in the queue at distance 0
dist = bfs (Q.fromList zeros) (M.fromList [(z, 0) | z <- zeros])
bfs Empty d = d
bfs ((r, c) :<| q) d =
let next = [p | p@(a, b) <- [(r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)],
a >= 0, b >= 0, a < rows, b < cols, not (M.member p d)]
d' = foldr (\p -> M.insert p (d M.! (r, c) + 1)) d next
in bfs (foldl (|>) q next) d'
Variants to try
- Some cells are walls that cannot be crossed: skip them in the search; Manhattan distance no longer works.
- Return, for each cell, which zero is nearest (carry the source along in the queue).
- Distances are Euclidean: the two-pass distance transform, or a different algorithm altogether.
Next problems
Shortest path through a grid
Breadth-first search reaches every cell in the fewest moves because it explores in rings of equal distance. Why it beats depth-first search is the lesson.
Open the problem → Medium18 minMinutes until every orange rots
Start the search from every rotten orange at once. The answer is the number of rings it takes to reach the last fresh one.
Open the problem → Hard30 minShortest word transformation
Change one letter at a time, using only dictionary words, from a start word to an end word. Each word is a node; breadth-first search finds the shortest ladder.
Open the problem →Write a solution and run it against the real test table.
Keep going
Shortest path through a grid
Breadth-first search reaches every cell in the fewest moves because it explores in rings of equal distance. Why it beats depth-first search is the lesson.
Open the problem → Medium18 minMinutes until every orange rots
Start the search from every rotten orange at once. The answer is the number of rings it takes to reach the last fresh one.
Open the problem → Hard30 minShortest word transformation
Change one letter at a time, using only dictionary words, from a start word to an end word. Each word is a node; breadth-first search finds the shortest ladder.
Open the problem → GraphsThe pattern behind it
Grids, networks and dependencies are all graphs. Breadth-first search finds shortest paths, depth-first search and union-find find what is connected, Dijkstra handles weighted edges, and a queue of nodes with no remaining prerequisites gives an order.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.