Opening the reading…
Opening the reading…
MATRIX › MATRIX TRANSFORMATION AND MODIFICATION
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A zero at position (i, j) affects exactly two things: every cell in row i and every cell in column j. You could record affected rows and columns in separate arrays, but the matrix already has one convenient cell in each row and column that can carry this information: matrix[i][0] marks row i, and matrix[0][j] marks column j.
The first row and first column create one collision. A zero in the first row should mark the entire first row, while matrix[0][j] is also being used as the marker for column j. The same collision occurs in the first column. Two Boolean flags preserve whether those boundary lines contained an original zero, so the matrix can safely serve as O(1)-space bookkeeping.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(m x n) | The interior is examined during the marking pass and the applying pass, while the boundary scans touch only O(m + n) cells. Each matrix cell is processed a constant number of times, so the total remains O(m x n). |
| Space | O(1) extra | Only two Boolean flags and a fixed number of indices are added beyond the matrix itself. The matrix is modified in place, so there is no returned array whose storage must be counted; the bound stays O(1) for every matrix shape. |
#include <vector>
using namespace std;
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
bool firstRowZero = false;
bool firstColZero = false;
for (int j = 0; j < n; ++j) {
if (matrix[0][j] == 0) {
firstRowZero = true;
break;
}
}
for (int i = 0; i < m; ++i) {
if (matrix[i][0] == 0) {
firstColZero = true;
break;
}
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if (matrix[i][j] == 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}
if (firstRowZero) {
for (int j = 0; j < n; ++j) {
matrix[0][j] = 0;
}
}
if (firstColZero) {
for (int i = 0; i < m; ++i) {
matrix[i][0] = 0;
}
}
}
};The order of the passes is the central insight. The first pass asks which cells were originally zero, while the second pass asks which cells must become zero. If you combine those jobs, a zero written during the solution process looks like an original zero and spreads farther than the statement requires.
A straightforward alternative stores one Boolean per row and one per column. First scan the matrix and mark every row and column containing an original zero, then scan it again and clear cells whose row or column is marked. This is often easier to explain and debug, but it uses O(m + n) extra space instead of the required O(1), so the boundary-marker solution is the space optimisation rather than a different time strategy.
#include <vector>
using namespace std;
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
vector<bool> zeroRow(m, false);
vector<bool> zeroCol(n, false);
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i][j] == 0) {
zeroRow[i] = true;
zeroCol[j] = true;
}
}
}
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (zeroRow[i] || zeroCol[j]) {
matrix[i][j] = 0;
}
}
}
}
};