HARD Lab · Matrix · 59

73. Set Matrix Zeroes

When a cell is zero, its entire row and column must be zeroed — and the trick is doing it without extra space by repurposing the first row and first column as in-place boolean flags.

MediumMatrixIn-place MarkersTypeScript

PROBLEM What we're solving

Given an m × n integer matrix, if any cell is 0, set its entire row and column to 0 — in place. For example:
Input: [[1,1,1],[1,0,1],[1,1,1]]
Output: [[1,0,1],[0,0,0],[1,0,1]]
The zero at [1][1] wipes row 1 and column 1.

KEY IDEA Use the matrix itself as the flag store

Insight → You need to remember which rows and columns to zero. Instead of an extra boolean array, borrow the first row and first column as your flag storage — matrix[0][c] = 0 means "zero column c", matrix[r][0] = 0means "zero row r". You just need to save whether the first row and first column themselves originally had a zero before you overwrite them.

RECIPE Four-step in-place algorithm

  • 0 · Pre-check. Scan row 0 and col 0 and record firstRowZero / firstColZero booleans. You need this before those cells become marker storage.
  • 1 · Mark (pass 1). Iterate interior cells (r≥1, c≥1). When matrix[r][c] === 0, set matrix[0][c] = 0 and matrix[r][0] = 0. These markers are why you need no extra array.
  • 2 · Zero interior (pass 2). Iterate interior cells again. If matrix[0][c] === 0 or matrix[r][0] === 0, set matrix[r][c] = 0.
  • 3 · Handle row 0 / col 0. If firstRowZero, zero all of row 0. If firstColZero, zero all of col 0. This step must come last; zeroing row 0 first would corrupt the flags.
Classic confusion →Beginners zero cells as they scan ("zero-as-you-go"). This corrupts unvisited cells: a freshly-written zero in row 3 looks like an original zero and incorrectly wipes more rows later. The two-pass approach prevents this by separating the discovery phase (mark only) from the write phase (zero only).

COST Complexity & space reduction

Extra boolean arrays
O(m + n)
Simple, but allocates two extra arrays.
In-place marker rows
O(1)
O(m·n) time; only 2 boolean variables extra.

Both approaches run in O(m·n) time (two full passes). The O(m+n) approach is easier to reason about; the O(1) approach is what interviewers ask for once the naive solution is on the board.

Pattern transfer → Using the input matrix as scratch storage appears in Game of Life (encode old and new state in the same cell using bit tricks), Rotate Image (transpose + reverse in place), and Spiral Matrix (in-place marking of visited cells). Any time space is tight and you need flags, look for spare bits or dedicated marker rows/cols in the data.

RUN IT Mark with row 0 / col 0, then zero the interior

step 0 / 16
STARTWalk the matrix to find zeros. We use row 0 and col 0 as in-place markers — no extra space.
1
1
1
1
0
1
1
1
1
Phase 1
Scan for zeros; mark first-row and first-col cells as markers.
current cell being scanned/writtenmarker cell (row 0 / col 0)
slowfast

TYPESCRIPT The solution, annotated

setMatrixZeroes.ts
function setZeroes(matrix: number[][]): void {
  const rows = matrix.length;
  const cols = matrix[0].length;

  // Remember whether row 0 / col 0 themselves need zeroing
  // BEFORE we overwrite them as marker storage.
  let firstRowZero = false;
  let firstColZero = false;
  for (let c = 0; c < cols; c++) if (matrix[0][c] === 0) firstRowZero = true;
  for (let r = 0; r < rows; r++) if (matrix[r][0] === 0) firstColZero = true;

  // Pass 1: scan interior; mark row 0 / col 0 as flags.
  for (let r = 1; r < rows; r++) {
    for (let c = 1; c < cols; c++) {
      if (matrix[r][c] === 0) {
        matrix[0][c] = 0;   // flag: column c needs zeroing
        matrix[r][0] = 0;   // flag: row r needs zeroing
      }
    }
  }

  // Pass 2: zero interior cells driven by the flags.
  for (let r = 1; r < rows; r++) {
    for (let c = 1; c < cols; c++) {
      if (matrix[0][c] === 0 || matrix[r][0] === 0) {
        matrix[r][c] = 0;
      }
    }
  }

  // Handle first row / col last (they were used as scratch).
  if (firstRowZero) {
    for (let c = 0; c < cols; c++) matrix[0][c] = 0;
  }
  if (firstColZero) {
    for (let r = 0; r < rows; r++) matrix[r][0] = 0;
  }
}

Reading it block by block

Lines 7–9 — pre-check row 0 and col 0.We're about to overwrite cells in these as flags. We must save whether they themselveshad a zero before we touch them, or we'd lose that information.
Lines 12–18 — Pass 1: mark. Only look at the interior (r≥1, c≥1). A zero at [r][c] sets matrix[0][c] (flag for column c) and matrix[r][0] (flag for row r). No cell is zeroed yet — pure discovery.
Lines 21–25 — Pass 2: zero interior. Re-visit interior cells. If either the column flag matrix[0][c] or the row flag matrix[r][0] is zero, this cell must be zeroed. Because we wrote the flags in a prior pass, no confusion from freshly-written zeros.
Lines 28–32 — Handle row 0 / col 0 last. These were our scratch space. firstRowZero and firstColZero — captured before pass 1 — now tell us whether to wipe them. Doing this last ensures the flags remain valid throughout pass 2.
Complexity → O(m·n) time (two complete passes), O(1) extra space (two booleans). The naive O(m+n) space version is fine as a first cut; always name the space reduction when asked.

INTERVIEWFollow-ups they'll ask

  • "Walk me through the O(m+n) approach first." Keep a Set<number> of zero rows and another of zero columns, then do a second pass to zero them. Easy to code, clear to explain — good starting point before optimizing to O(1).
  • "Why not zero as you find each zero?" Zeroing cell [r][c]propagates zeros into cells you haven't visited yet, causing those to incorrectly trigger more row/column wipes. The two-pass design eliminates this contamination.
  • "What if the matrix has no zeros?" The first pass sets no flags, the second pass changes nothing, and both booleans are false. The algorithm handles it correctly with zero writes.
  • "Can you use a single extra variable instead of two booleans?" Yes — encode first-col state in a separate variable while using matrix[0][0] to represent row 0. One less boolean, same logic.
  • "Related: Game of Life." Same in-place two-pass idea — encode the next state into spare bits of the current integer value so the old state is still readable during the scan, then strip the encoding in a second pass.

MNEMONIC The one-liner

"Borrow row 0 and col 0 as flags — but remember if they were already zero before you borrow them."

TRIGGERS When you see ___ → reach for ___

"set entire row and column to zero"two-pass in-place markers
matrix mutation, O(1) space requestedrepurpose first row/col as flags
corrupt-if-you-write-while-scanningseparate mark pass from write pass
"in place" + boolean state per row/colrow 0 = col flags, col 0 = row flags

SKELETON The reusable shape

skeleton.ts
// 1. Check if row 0 / col 0 originally contain a zero.
let firstRowZero = false, firstColZero = false;
// ... scan row 0 and col 0 ...

// 2. Scan interior; mark row 0 / col 0.
for (let r = 1; r < rows; r++)
  for (let c = 1; c < cols; c++)
    if (matrix[r][c] === 0) { matrix[0][c] = 0; matrix[r][0] = 0; }

// 3. Zero interior using the flags.
for (let r = 1; r < rows; r++)
  for (let c = 1; c < cols; c++)
    if (matrix[0][c] === 0 || matrix[r][0] === 0) matrix[r][c] = 0;

// 4. Handle first row / col (must be last).
if (firstRowZero) { /* zero row 0 */ }
if (firstColZero) { /* zero col 0 */ }

FLASHCARDS Tap to flip

Why not just zero rows/cols immediately when you find a zero?
Newly written zeros corrupt unvisited cells, causing more rows/cols to be zeroed incorrectly. Separate discovery from writing.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
What is the extra space complexity of the optimal in-place solution?
QUESTION 02
Why is "zero each cell's row and column as you find zeros" wrong?
QUESTION 03
For input [[0,1,2,0],[3,4,5,2],[1,3,1,5]], which cells act as markers after pass 1?
QUESTION 04
Why must the first row and first column be handled after the interior zeroing pass?
QUESTION 05
Trace: input is [[1,1,1],[1,0,1],[1,1,1]]. What is the output?
QUESTION 06
What is the time complexity of this algorithm?
QUESTION 07
If the matrix has no zeros at all, how many writes does the algorithm perform?