73. Set Matrix Zeroes - Explanation
Description
Given an m x n matrix of integers matrix, if an element is 0, set its entire row and column to 0's.
You must update the matrix in-place.
Follow up: Could you solve it using O(1) space?
Example 1:
Input: matrix = [
[0,1],
[1,0]
]
Output: [
[0,0],
[0,0]
]Example 2:
Input: matrix = [
[1,2,3],
[4,0,5],
[6,7,8]
]
Output: [
[1,0,3],
[0,0,0],
[6,0,8]
]Constraints:
1 <= matrix.length, matrix[0].length <= 100-2^31 <= matrix[i][j] <= (2^31) - 1
Topics
Recommended Time & Space Complexity
You should aim for a solution with O(m*n) time and O(1) space, where m is the number of rows and n is the number of columns in the given matrix.
Hint 1
A brute force approach would iterate through the given matrix and update the corresponding row and column in a new matrix on the fly. This would result in an O((m*n)*(m+n)) time and O(m*n) space solution. Can you think of a better way? Maybe you should consider using a single variable for a row and a single variable for a column instead of updating entire row and column.
Hint 2
A better approach is to use O(m+n) boolean arrays. We iterate through the matrix, and when encountering a zero, we mark the respective row and column as true. In the second iteration, we set a cell to 0 if its corresponding row or column is marked true. Can you think of a way to optimize the space further?
Hint 3
We can use the topmost row and leftmost column of the matrix as boolean arrays by marking 0 instead of true. However, since they overlap at one cell, we use a single variable to track the top row separately. We then iterate through the matrix and mark zeros accordingly.
Hint 4
In the second iteration, we update all cells that are not part of the top row or left column accordingly. After making the necessary changes, we check the top-leftmost cell and update the corresponding column. Finally, we check the extra variable and update the top row accordingly.
Prerequisites
Before attempting this problem, you should be comfortable with:
- 2D Arrays / Matrices - Traversing and modifying elements in a matrix using row and column indices
- In-Place Modification - Updating data structures without using extra space proportional to input size
- Marker Techniques - Using parts of the input as flags to track state during processing
1. Brute Force
Intuition
We need to modify the matrix so that if any cell is 0, then its entire row and entire column become 0.
The main challenge is that if we change cells to 0 while scanning, those newly created zeros could incorrectly force more rows/columns to be zeroed.
To avoid this, the brute force approach uses a separate copy of the matrix:
- we read zeros from the original matrix
- we write the row/column changes into the copy
- at the end, we copy the final values back
This keeps the logic simple and prevents accidental cascading updates.
Algorithm
- Let
ROWSandCOLSbe the matrix dimensions. - Create a copy matrix
markwith the same values as the original matrix. - Traverse every cell
(r, c)in the original matrix:- If
matrix[r][c] == 0:- set all cells in row
rofmarkto0 - set all cells in column
cofmarkto0
- set all cells in row
- If
- After processing all zeros, copy every value from
markback intomatrix. - The original
matrixis now updated correctly.
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
ROWS, COLS = len(matrix), len(matrix[0])
mark = [[matrix[r][c] for c in range(COLS)] for r in range(ROWS)]
for r in range(ROWS):
for c in range(COLS):
if matrix[r][c] == 0:
for col in range(COLS):
mark[r][col] = 0
for row in range(ROWS):
mark[row][c] = 0
for r in range(ROWS):
for c in range(COLS):
matrix[r][c] = mark[r][c]Time & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and is the number of columns.
2. Iteration
Intuition
We need to update the matrix so that if a cell is 0, then its entire row and entire column are set to 0.
The key challenge is to avoid modifying the matrix too early.
If we directly set rows and columns to 0 while scanning, newly created zeros could incorrectly trigger more rows and columns to be zeroed.
To handle this safely, we split the process into two passes:
- First pass: record which rows and columns need to be zeroed using
rowsandcolsflags - Second pass: apply the zeroing based on that record
We use two helper arrays:
rows[r]→ whether rowrshould be zeroedcols[c]→ whether columncshould be zeroed
This keeps the logic clean and easy to reason about.
Algorithm
- Let
ROWSandCOLSbe the dimensions of the matrix. - Create two boolean arrays:
rowsof sizeROWS, initialized tofalsecolsof sizeCOLS, initialized tofalse
- Traverse the matrix:
- If
matrix[r][c] == 0:- mark
rows[r] = true - mark
cols[c] = true
- mark
- If
- Traverse the matrix again:
- If
rows[r]istrueorcols[c]istrue:- set
matrix[r][c] = 0
- set
- If
- The matrix is now correctly updated.
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
ROWS, COLS = len(matrix), len(matrix[0])
rows, cols = [False] * ROWS, [False] * COLS
for r in range(ROWS):
for c in range(COLS):
if matrix[r][c] == 0:
rows[r] = True
cols[c] = True
for r in range(ROWS):
for c in range(COLS):
if rows[r] or cols[c]:
matrix[r][c] = 0Time & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and is the number of columns.
3. Iteration (Space Optimized)
Intuition
We need to set an entire row and column to 0 if any cell in that row or column is 0.
The common two-array solution uses extra space to remember which rows/columns should be zeroed.
To optimize space, we can reuse the matrix itself as the "marker storage":
- Use the first row to mark which columns should become zero
- Use the first column to mark which rows should become zero
One complication:
matrix[0][0]sits at the intersection of the first row and first column, so it can't independently represent both.- Also, we must separately track whether the first row originally contained a zero.
That's why we keep a boolean rowZero:
rowZero = truemeans the first row must be zeroed at the end.
Algorithm
- Initialize
rowZero = false. - First pass (mark rows and columns):
- Traverse every cell
(r, c):- If
matrix[r][c] == 0:- mark the column by setting
matrix[0][c] = 0 - if
r > 0, mark the row by settingmatrix[r][0] = 0 - if
r == 0, setrowZero = true(first row needs to be zeroed)
- mark the column by setting
- If
- Traverse every cell
- Second pass (apply markers to the inner matrix):
- For
rfrom1toROWS - 1:- For
cfrom1toCOLS - 1:- If
matrix[0][c] == 0ormatrix[r][0] == 0, setmatrix[r][c] = 0
- If
- For
- For
- Handle the first column:
- If
matrix[0][0] == 0, zero out the entire first column
- If
- Handle the first row:
- If
rowZeroistrue, zero out the entire first row
- If
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
ROWS, COLS = len(matrix), len(matrix[0])
rowZero = False
for r in range(ROWS):
for c in range(COLS):
if matrix[r][c] == 0:
matrix[0][c] = 0
if r > 0:
matrix[r][0] = 0
else:
rowZero = True
for r in range(1, ROWS):
for c in range(1, COLS):
if matrix[0][c] == 0 or matrix[r][0] == 0:
matrix[r][c] = 0
if matrix[0][0] == 0:
for r in range(ROWS):
matrix[r][0] = 0
if rowZero:
for c in range(COLS):
matrix[0][c] = 0Time & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and is the number of columns.
Common Pitfalls
Modifying the Matrix While Scanning
Setting cells to zero while still scanning causes cascading updates where newly created zeros trigger more rows and columns to be zeroed. This produces incorrect results because the algorithm cannot distinguish between original zeros and zeros created during processing.
Forgetting to Track the First Row Separately
In the space-optimized approach, matrix[0][0] serves double duty for marking both the first row and first column. Using a single variable to track both leads to incorrect results. A separate boolean is needed to track whether the first row originally contained a zero.
Incorrect Order of Final Updates
When using the first row and column as markers, the order of applying updates matters. If you zero the first row or column too early, you lose the marker information needed to process the rest of the matrix. Always update the inner cells first, then handle the first row and column last.
Sign in to join the discussion