01 Matrix
MedPrompt
Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell.
The distance between two cells sharing a common edge is 1.
Example 1:
Input: mat = [[0,0,0],[0,1,0],[0,0,0]]
Output: [[0,0,0],[0,1,0],[0,0,0]]Example 2:
Input: mat = [[0,0,0],[0,1,0],[1,1,1]]
Output: [[0,0,0],[0,1,0],[1,2,1]]
Constraints:
m == mat.lengthn == mat[i].length1 <= m, n <= 1041 <= m * n <= 104mat[i][j]is either0or1.- There is at least one
0inmat.
Note: This question is the same as 1765: https://leetcode.com/problems/map-of-highest-peak/
You can check more questions like these at Prachub
Approaches
3 approaches with complexity analysis and trade-offs.
This is a straightforward but highly inefficient approach. The idea is to iterate through every cell of the matrix. If a cell contains a 1, we then perform another complete scan of the entire matrix to find the minimum distance to a cell containing a 0.
Algorithm
- Create a result matrix
distof the same size as the inputmat. - Iterate through each cell
(r, c)of the input matrix. - If
mat[r][c]is0, the distance is0, so setdist[r][c] = 0. - If
mat[r][c]is1, we must find the nearest0. Initialize a variablemin_distto a very large value. - Start a second, nested traversal over all cells
(i, j)of the matrix. - If
mat[i][j]is0, calculate the Manhattan distance|r - i| + |c - j|and updatemin_distwith the minimum value found so far. - After checking all cells,
dist[r][c]is set to the finalmin_dist. - Return the
distmatrix.
Walkthrough
We create a new result matrix, dist, of the same dimensions as the input mat. We traverse each cell (r, c) of the input matrix. If mat[r][c] is 0, the distance is 0, so we set dist[r][c] = 0. If mat[r][c] is 1, we must find the nearest 0. We initialize a variable min_dist to a very large value. Then, we start a second, nested traversal over all cells (i, j) of the matrix. If we find a cell mat[i][j] that is 0, we calculate the Manhattan distance |r - i| + |c - j| and update min_dist with the minimum value found so far. After checking all cells, dist[r][c] is set to the final min_dist. This process is repeated for every cell containing a 1.
class Solution { public int[][] updateMatrix(int[][] mat) { int m = mat.length; int n = mat[0].length; int[][] dist = new int[m][n]; for (int r = 0; r < m; r++) { for (int c = 0; c < n; c++) { if (mat[r][c] == 0) { dist[r][c] = 0; } else { int min_dist = Integer.MAX_VALUE; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (mat[i][j] == 0) { min_dist = Math.min(min_dist, Math.abs(r - i) + Math.abs(c - j)); } } } dist[r][c] = min_dist; } } } return dist; }}Complexity
Time
O((m * n)^2) - For each of the `m*n` cells, we might iterate through all `m*n` cells again in the worst case.
Space
O(m * n) - To store the result matrix. If modifying the input matrix were allowed, it would be O(1) extra space.
Trade-offs
Pros
Simple to understand and implement.
Cons
Extremely slow due to its nested loops.
Will result in a 'Time Limit Exceeded' error on most platforms for the given constraints.
A much more efficient approach uses dynamic programming. The distance of a cell to the nearest 0 depends on the distances of its neighbors. We can't solve this with a single pass because of dependencies in all four directions. However, we can solve it with two passes over the matrix: one from top-left to bottom-right, and another from bottom-right to top-left.
Algorithm
- Initialize a
distmatrix of sizem x n. For cells with0inmat, setdistto0. For cells with1, setdistto a large value (e.g.,m + n). - Pass 1 (Top-Left to Bottom-Right):
- For
ifrom0tom-1:- For
jfrom0ton-1:- If
i > 0, updatedist[i][j] = min(dist[i][j], dist[i-1][j] + 1). - If
j > 0, updatedist[i][j] = min(dist[i][j], dist[i][j-1] + 1).
- If
- For
- For
- Pass 2 (Bottom-Right to Top-Left):
- For
ifromm-1down to0:- For
jfromn-1down to0:- If
i < m-1, updatedist[i][j] = min(dist[i][j], dist[i+1][j] + 1). - If
j < n-1, updatedist[i][j] = min(dist[i][j], dist[i][j+1] + 1).
- If
- For
- For
- Return
dist.
Walkthrough
The first pass goes from top-left to bottom-right. It calculates the distance for each cell (i, j) based on the distances of the cells above it (i-1, j) and to its left (i, j-1). This effectively propagates distances from 0s that are located to the top-left of the current cell. The second pass goes from bottom-right to top-left. It updates the distance for each cell (i, j) based on the distances of the cells below it (i+1, j) and to its right (i, j+1). This pass propagates distances from 0s located to the bottom-right. By combining these two passes, we ensure that for every cell, we have considered the shortest path from a 0 regardless of its relative position.
class Solution { public int[][] updateMatrix(int[][] mat) { int m = mat.length; int n = mat[0].length; int[][] dist = new int[m][n]; int maxDist = m + n; // A safe upper bound for any distance // First pass: top-left to bottom-right for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (mat[i][j] == 0) { dist[i][j] = 0; } else { dist[i][j] = maxDist; if (i > 0) { dist[i][j] = Math.min(dist[i][j], dist[i - 1][j] + 1); } if (j > 0) { dist[i][j] = Math.min(dist[i][j], dist[i][j - 1] + 1); } } } } // Second pass: bottom-right to top-left for (int i = m - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { if (i < m - 1) { dist[i][j] = Math.min(dist[i][j], dist[i + 1][j] + 1); } if (j < n - 1) { dist[i][j] = Math.min(dist[i][j], dist[i][j + 1] + 1); } } } return dist; }}Complexity
Time
O(m * n) - We perform two full passes over the matrix.
Space
O(m * n) - For the `dist` matrix.
Trade-offs
Pros
Very efficient with linear time complexity.
Does not require an explicit queue data structure, which can sometimes lead to better cache performance.
Cons
The logic can be less intuitive to come up with compared to a standard BFS.
This is a classic and highly intuitive approach for shortest path problems on unweighted graphs (like a grid). Instead of searching from each 1 for a 0, we can reverse the problem: find the shortest path from any 0 to all other cells. This can be modeled as a multi-source Breadth-First Search (BFS), where all 0s are the initial sources.
Algorithm
- Get matrix dimensions
mandn. - Create a
distmatrix, initialized with0for0-cells and a marker for unvisited (e.g., -1) for1-cells. - Create a queue and add the coordinates of all
0-cells to it. - While the queue is not empty:
- Dequeue a cell
(r, c). - For each of its four neighbors
(nr, nc):- If the neighbor is within bounds and is unvisited (
dist[nr][nc] == -1):- Set its distance:
dist[nr][nc] = dist[r][c] + 1. - Enqueue the neighbor
(nr, nc).
- Set its distance:
- If the neighbor is within bounds and is unvisited (
- Dequeue a cell
- Return the
distmatrix.
Walkthrough
We start by identifying all the 'source' cells, which are the cells containing 0. Their distance to the nearest 0 is 0. We add all these source cells to a queue. We also initialize a dist matrix. We can use the input matrix itself to store distances if modification is allowed, or create a new one. Cells with 0 have distance 0, and cells with 1 can be marked as unvisited (e.g., with a value of -1). Then, we perform a standard BFS. We dequeue a cell, and for each of its unvisited neighbors, we update their distance to be the current cell's distance + 1, and then enqueue them. Because BFS explores layer by layer, the first time we reach a cell, we are guaranteed to have found the shortest path to it from one of the initial 0 sources.
import java.util.LinkedList;import java.util.Queue; class Solution { public int[][] updateMatrix(int[][] mat) { int m = mat.length; int n = mat[0].length; int[][] dist = new int[m][n]; Queue<int[]> queue = new LinkedList<>(); // Initialize dist matrix and queue with all 0s for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (mat[i][j] == 0) { dist[i][j] = 0; queue.offer(new int[]{i, j}); } else { dist[i][j] = -1; // Mark as unvisited } } } int[] dr = {-1, 1, 0, 0}; int[] dc = {0, 0, -1, 1}; while (!queue.isEmpty()) { int[] cell = queue.poll(); int r = cell[0]; int c = cell[1]; for (int i = 0; i < 4; i++) { int nr = r + dr[i]; int nc = c + dc[i]; // Check bounds and if the cell is unvisited if (nr >= 0 && nr < m && nc >= 0 && nc < n && dist[nr][nc] == -1) { dist[nr][nc] = dist[r][c] + 1; queue.offer(new int[]{nr, nc}); } } } return dist; }}Complexity
Time
O(m * n) - Each cell is enqueued and dequeued exactly once.
Space
O(m * n) - In the worst case, the queue can hold up to `m*n` cells (e.g., a checkerboard pattern). The `dist` matrix also takes `O(m*n)` space.
Trade-offs
Pros
Very efficient with linear time complexity.
A standard, intuitive pattern for shortest path problems on grids.
Easily adaptable to similar problems.
Cons
Requires an explicit queue, which might have slightly more overhead than the DP approach's simple array traversals.
Solutions
Solution
class Solution {public int[][] updateMatrix(int[][] mat) { int m = mat.length, n = mat[0].length; int[][] ans = new int[m][n]; for (int[] row : ans) { Arrays.fill(row, -1); } Deque<int[]> q = new ArrayDeque<>(); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (mat[i][j] == 0) { q.offer(new int[]{i, j}); ans[i][j] = 0; } } } int[] dirs = {-1, 0, 1, 0, -1}; while (!q.isEmpty()) { int[] p = q.poll(); int i = p[0], j = p[1]; for (int k = 0; k < 4; ++k) { int x = i + dirs[k], y = j + dirs[k + 1]; if (x >= 0 && x < m && y >= 0 && y < n && ans[x][y] == -1) { ans[x][y] = ans[i][j] + 1; q.offer(new int[]{x, y}); } } } return ans; }}class Solution { public: vector < vector < int >> updateMatrix ( vector < vector < int >>& mat ) { int m = mat . size (), n = mat [ 0 ]. size (); vector < vector < int >> ans ( m , vector < int > ( n , - 1 )); queue < pair < int , int >> q ; for ( int i = 0 ; i < m ; ++ i ) { for ( int j = 0 ; j < n ; ++ j ) { if ( mat [ i ][ j ] == 0 ) { ans [ i ][ j ] = 0 ; q . emplace ( i , j ); } } } vector < int > dirs = { - 1 , 0 , 1 , 0 , - 1 }; while ( ! q . empty ()) { auto p = q . front (); q . pop (); for ( int i = 0 ; i < 4 ; ++ i ) { int x = p . first + dirs [ i ]; int y = p . second + dirs [ i + 1 ]; if ( x >= 0 && x < m && y >= 0 && y < n && ans [ x ][ y ] == - 1 ) { ans [ x ][ y ] = ans [ p . first ][ p . second ] + 1 ; q . emplace ( x , y ); } } } return ans ; } };class Solution: def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]: m, n = len(mat), len(mat[0]) ans = [[- 1] * n for _ in range(m)] q = deque() for i, row in enumerate(mat): for j, x in enumerate(row): if x == 0: ans[i][j] = 0 q . append((i, j)) dirs = (- 1, 0, 1, 0, - 1) while q: i, j = q . popleft() for a, b in pairwise(dirs): x, y = i + a, j + b if 0 <= x < m and 0 <= y < n and ans[x][y] == - 1: ans[x][y] = ans[i][j] + 1 q . append((x, y)) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.