Problem
Given an m x n matrix of 0s and 1s, provide an algorithm such that if an element is 0, its entire row and column is set to 0
OR
Given an m x n
integer matrix matrix
, if an element is 0
, set its entire row and column to 0
’s.
You must do it in place.
Follow Up:
Can you do it in one pass?
Examples
Example 1:
$$ \Huge \begin{array}{|c|c|c|c|c|} \hline 1 & \colorbox{orange} 0 & 1 & 1 & \colorbox{orange} 0 \\ \hline \colorbox{orange} 0 & 1 & 1 & 1 & 1 \\ \hline 1 & 1 & 1 & 1 & 1 \\ \hline 1 & \colorbox{orange} 0 & 1 & 1 & 1 \\ \hline 1 & 1 & 1 & 1 & 1 \\ \hline \end{array} \Longrightarrow \begin{array}{|c|c|c|c|c|} \hline \colorbox{yellow} 0 & \colorbox{orange} 0 & \colorbox{yellow} 0 & \colorbox{yellow} 0 & \colorbox{orange} 0 \\ \hline \colorbox{orange} 0 & \colorbox{yellow} 0 & \colorbox{yellow} 0 & \colorbox{yellow} 0 & \colorbox{yellow} 0 \\ \hline \colorbox{yellow} 0 & \colorbox{yellow} 0 & 1 & 1 & \colorbox{yellow} 0 \\ \hline \colorbox{yellow} 0 & \colorbox{orange} 0 & \colorbox{yellow} 0 & \colorbox{yellow} 0 & \colorbox{yellow} 0 \\ \hline \colorbox{yellow} 0 & \colorbox{yellow} 0 & 1 & 1 & \colorbox{yellow} 0 \\ \hline \end{array} $$
|
|
Example 2:
$$ \Huge \begin{array}{|c|c|c|c|} \hline \colorbox{orange} 0 & 1 & 2 & \colorbox{orange} 0 \\ \hline 3 & 4 & 5 & 2 \\ \hline 1 & 3 & 1 & 5 \\ \hline \end{array} \Longrightarrow \begin{array}{|c|c|c|c|} \hline \colorbox{orange} 0 & \colorbox{yellow} 0 & \colorbox{yellow} 0 & \colorbox{orange} 0 \\ \hline \colorbox{yellow} 0 & 4 & 5 & \colorbox{yellow} 0 \\ \hline \colorbox{yellow} 0 & 3 & 1 & \colorbox{yellow} 0 \\ \hline \end{array} $$
|
|
Solution
Video explanation
Here is the video explaining below methods in detail. Please check it out:
Method 1 - Naive - Using copy matrix
In the naive approach, we solve the problem by creating a copy of the input matrix. This duplicate is used to track which rows and columns need modification. By using a separate matrix, we avoid overwriting the original matrix during the process.
Approach
- Create a Copy Matrix:
- First, create a new matrix (let’s call it
copyMatrix
) of the same size as the input matrix, and copy all values from the input matrix into this new matrix.
- First, create a new matrix (let’s call it
- Iterate and Record:
- Traverse through the original matrix and identify any element with the value
0
. - For each
0
found in the original matrix, set its corresponding row and column in thecopyMatrix
to0
.
- Traverse through the original matrix and identify any element with the value
- Update Original Matrix:
- Once the entire
copyMatrix
is updated, use its values to overwrite the original matrix.
- Once the entire
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(m*n(m + n))
.- Copy Matrix Creation:
O(m * n)
wherem
is the number of rows andn
is the number of columns. - Traversal and Marking: For each element in the matrix, traverse its corresponding row and column, leading to a worst-case time complexity of O(m * n * (m + n)) for larger matrices. Thus, time complexity is
O(m * n * (m + n))
.
- Copy Matrix Creation:
- 🧺 Space complexity:
O(m*n)
as the additionalcopyMatrix
requiresO(m * n)
space.
Method 2 - Using the set as Extra Space
In this approach we can do :
- Scan the matrix, store the numbers of rows and columns that will be set to zero to two sets
- Iterate through those two sets and set corresponding row and col to zeros.
Code
|
|
Complexity
- ⏰ Time complexity:
O(m*n)
- 🧺 Space complexity:
O(m+n)
Method 3 - Using boolean array as extra space
Also, as SET takes more space, rather than using SET, we can use a simple boolean arrays.
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(m*n)
- 🧺 Space complexity:
O(m+n)
Method 4 - Using first row and column as boolean array and use 2 flags
This method is a space optimized version of above method 1. This method uses the first row and first column of the input matrix in place of the auxiliary arrays rowMarker
and colMarker
of method 1.
So what we do is: first take care of first row and column and store the info about these two in two flag variables rowFlag and colFlag. Once we have this info, we can use first row and first column as auxiliary.
We now need 2 passes.
- I use first column and first row as markers to know where are rows/cols with only 1’s. Then, there are 2 variables
r
andc
to remember if first row/column are all 1’s also. So the first pass sets the markers and resets the rest to 0’s. - The second pass sets 1 in places where rows and cols where marked to be 1, and resets 1st line/col depending on
r
andc
.
Now apply method 1 for submatrix (matrix excluding first row and first column) of size (M-1)*(N-1)
:
- Scan the first row and set a variable rowFlag to indicate whether we need to set all 0s in first row or not.
- Scan the first column and set a variable colFlag to indicate whether we need to set all 0s in first column or not.
- Use first row and first column as the auxiliary arrays row[] and col[] respectively, consider the matrix as submatrix starting from second row and second column and apply method 1.
- Finally, using rowFlag and colFlag, update first row and first column if needed.
Pseudocode
|
|
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(m*n)
- 🧺 Space complexity:
O(1)
Method 5 - Using just 1 flag
Intuition
While the two-flag approach works perfectly, we realise that the two flags (firstRowHasZero
and isFirstColZero
) add unnecessary complexity.
By assigning the marker array for rows to the first column and for columns to the first row, we can store all necessary information for the sub-matrix. However, this creates ambiguity at the common cell (0, 0)
(matrix[0][0]
), where it’s unclear whether it belongs to the row marker or column marker.
Resolving Ambiguity at (0, 0)
Scenario 1: Zero in the First Row
Consider the matrix where the 0
exists only in the first row:
|
|
The 0
at matrix[0][2]
would cause us to set the marker at matrix[0][0]
to 0
, representing the row marker.
Scenario 2: Zero in the First Column
Consider a different matrix where the 0
exists only in the first column:
|
|
The 0
at matrix[2][0]
would also cause us to set matrix[0][0]
to 0
, representing the column marker.
To resolve this ambiguity, we assign (0, 0)
(matrix[0][0]
) exclusively to the row marker array and eliminate the need for the firstRowZero
flag, retaining only the firstColZero
flag.
To optimise further, we leverage the matrix itself as a marker and use only one flag (isFirstColZero
). This flag handles the first column separately, avoiding ambiguity over the shared marker cell matrix[0][0]
.
How Do We Resolve This?
So, to avoid this ambiguity,
- The first row (using
matrix[0][j]
markers) can represent all column markers. - The first column (using
matrix[i][0]
markers) can represent all row markers. - The common cell
matrix[0][0]
represents row markers by default, and the separate flag (isFirstColZero
) efficiently handles the first column.
Approach
- Step 1: Check the First Column:
- Start by scanning the first column (
matrix[i][0]
) to check if it contains a0
. - If any cell in the first column is
0
, set the flagisFirstColZero = True
.
- Start by scanning the first column (
- Step 2: Mark Rows and Columns Using the Matrix Itself:
- Traverse the rest of the matrix (excluding the first row and the first column).
- If a cell
matrix[i][j]
is0
:- Mark its row by setting
matrix[i][0] = 0
. - Mark its column by setting
matrix[0][j] = 0
.
- Mark its row by setting
- These markers in the first row and column will later be used to update the matrix.
- Step 3: Update Rows and Columns:
- Update Rows:
- Traverse all rows starting from
1
onwards. - For any row
i
, if the marker in the first column (matrix[i][0]
) is0
, set all elements in that row to0
.
- Traverse all rows starting from
- Update Columns:
- Traverse all columns starting from
1
onwards. - For any column
j
, if the marker in the first row (matrix[0][j]
) is0
, set all elements in that column to0
.
- Traverse all columns starting from
- Update Rows:
- Step 4: Handle the First Row and First Column:
- Handle the First Row:
- If the marker at
matrix[0][0]
is0
, set all elements in the first row to0
.
- If the marker at
- Handle the First Column:
- If
isFirstColZero = True
, set all elements in the first column to0
.
- If
- Handle the First Row:
Code
|
|
|
|
Method 6 - Doing it in One Pass
So the idea is to use the values in the last row/column as a flag to indicate whether all of the values in the corresponding column/row are 1s. Using a Zig Zag scan through the entire matrix EXCEPT the final row/column. At each element, you set the value in the final row/column as to the logical AND of itself with the value in the current element. In other words, if you hit a 0, the final row/column will be set to 0. If you hit a 1, the value in the final row/column will be 1 only if it was 1 already. In any case set the current element to 0.
When you’ve finished, your final row/column should have 1s iff the corresponding column/row was filled with 1s.
Do a linear scan through the final row and column and looking for 1s. Set 1s in the corresponding elements in body of the matrix where the final row and column are both 1s.
Coding it will be tricky to avoid off-by-one errors etc but it should work in one pass.