Skip to content

Coding patternsBlind 75

Set Matrix Zeroes

Find the zeros first and clear later, and store the marks in the grid's own first row and column. Solved three ways, a full copy with O(m·n) space, row and column sets with O(m + n) space, and in-place markers with O(1) space, with a step-through, code in five languages and the Big O worked out from the code.

You are given a grid matrix with m rows and n columns (row_count and column_count in the code). Wherever a cell holds 0, set its whole row and its whole column to 0. Change the grid in place.

Only the zeros that were there at the start count. A cell that becomes 0 because of this rule does not clear its own row and column.

Example 1

Input: matrix = [[4, 2, 7, 1], [3, 0, 5, 8], [6, 9, 2, 0]]

Output: [[4, 0, 7, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

The 0 at row 1, column 1 clears row 1 and column 1. The 0 at row 2, column 3 clears row 2 and column 3.

c0c1c2c3
r04070
r10000
r20000

Example 2

Input: matrix = [[5, 3], [2, 0]]

Output: [[5, 0], [0, 0]]

Only the bottom row and the right column are cleared.

Example 3

Input: matrix = [[0, 6, 2]]

Output: [[0, 0, 0]]

One row: the 0 clears all of it.

  • 1 ≤ m, n ≤ 200
  • −2³¹ ≤ matrix[i][j] ≤ 2³¹ − 1
Do new zeros spread further?
No. Only the zeros in the original grid clear rows and columns.
Must it be in place?
Yes. The grid itself changes. The follow-up asks for O(1) extra memory.
Can any value be used as a marker?
No. Values cover the whole 32-bit range, so there is no spare value to mean "clear me".
Is the grid square?
Not always. m and n can differ.

The danger is clearing too early: a new zero looks like an original one. So the work splits in two: first record which rows and columns to clear, then clear. The only question is where to keep the record. The grid’s own first row and first column can hold it, which is a classic in-place matrix trick.

Markers in the grid O(m·n), O(1) space

Keep the two sets inside the grid. Row 0 is the column set: matrix[0][j] = 0 means “clear column j”. Column 0 is the row set: matrix[i][0] = 0 means “clear row i”.

Those cells hold real data too, so first save two flags: did row 0 have a 0, and did column 0 have a 0? Then:

  1. For every cell outside row 0 and column 0 that is 0, set its two markers.
  2. For every cell outside row 0 and column 0, clear it if its row marker or column marker is 0.
  3. Clear row 0 and column 0 last, using the two flags.

Why the order matters: row 0 and column 0 hold the markers, so they must be cleared last. If they went first, a cleared marker row would make every column look marked.

c0c1c2c3
r04271
r13058
r26920
first_row_zero
false
first_column_zero
false
  1. first_row_zero = 0 in matrix[0]
  2. first_column_zero = any(row[0] == 0 for row in matrix)
  3. for i in range(1, row_count):
  4. for j in range(1, column_count):
  5. if matrix[i][j] == 0: matrix[i][0] = matrix[0][j] = 0
  6. for i in range(1, row_count):
  7. for j in range(1, column_count):
  8. if matrix[i][0] == 0 or matrix[0][j] == 0: matrix[i][j] = 0
  9. if first_row_zero: set row 0 to 0
  10. if first_column_zero: set column 0 to 0

Use row 0 and column 0 as the sets. A 0 in column 0 marks a row to clear, and a 0 in row 0 marks a column. They hold real data too, so first remember if they had their own zeros.

1 / 8
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// Markers in the grid: row 0 and column 0 act as the column and row sets.
// Two passes over the cells: O(m * n) time, O(1) extra memory.
public void setZeroes(int[][] matrix) {
int rowCount = matrix.length, columnCount = matrix[0].length;
// Row 0 and column 0 will hold markers, so save their own zeros first.
boolean firstRowZero = false, firstColumnZero = false;
for (int j = 0; j < columnCount; j++) if (matrix[0][j] == 0) firstRowZero = true;
for (int i = 0; i < rowCount; i++) if (matrix[i][0] == 0) firstColumnZero = true;
for (int i = 1; i < rowCount; i++) {
for (int j = 1; j < columnCount; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
for (int i = 1; i < rowCount; i++) {
for (int j = 1; j < columnCount; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) matrix[i][j] = 0;
}
}
// Last, because until now they held the markers.
if (firstRowZero) for (int j = 0; j < columnCount; j++) matrix[0][j] = 0;
if (firstColumnZero) for (int i = 0; i < rowCount; i++) matrix[i][0] = 0;
}
}
Time O(m·n)Space O(1)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    Two double loops visit the inner cells, one check each: 1 step per run. Before them, two short loops read row 0 and column 0. After them, two short loops may clear them.

    1. first_row_zero = 0 in matrix[0]
    2. first_column_zero = any(row[0] == 0 for row in matrix)
    3. for i in range(1, row_count):
    4. for j in range(1, column_count):
    5. if matrix[i][j] == 0: matrix[i][0] = matrix[0][j] = 0
    6. for i in range(1, row_count):
    7. for j in range(1, column_count):
    8. if matrix[i][0] == 0 or matrix[0][j] == 0: matrix[i][j] = 0
    9. if first_row_zero: set row 0 to 0
    10. if first_column_zero: set column 0 to 0
  2. 2 Count the runs on Example 1

    Example 1 is 3 × 4. Checking row 0 reads 4 cells and column 0 reads 3. The inner part is 2 × 3 = 6 cells, visited once to mark and once to clear. Row 0 and column 0 had no zeros, so the last step does nothing.

    4 + 3 + 6 + 6 = 19 steps.

    partcheck row 0check col 0markclearrow 0 and col 0
    cells43660
    total
    4 + 3 + 6 + 6 = 19
  3. 3 Write the count for any n

    n + m + 2(m − 1)(n − 1), plus up to m + n at the end

    Check it with Example 1: 4 + 3 + 2 × 6 = 19.

  4. 4 Keep only the part that grows fastest

    Drop everything but 2·m·n, then drop the 2. The m and n terms are small next to m·n. What is left is m·n.

    So the time is O(m·n).

  5. S Space: count the extra memory the same way

    The code keeps two flags and the loop counters. The record of which rows and columns to clear lives inside the grid. The space is O(1).

    first_row_zerofirst_column_zeroij

2,000 random grids from 1 x 1 to 5 x 5 with many zeros, including zeros in row 0 and column 0. Full copy, row and column sets, and markers in the grid gave the same answer every time.

Largest inputsResult
A 200 x 200 grid (the most allowed), values across the whole 32-bit range, with zeros in row 0 and column 0. Markers in the grid.0.003 s, and under 0.001 MB of extra memory: two flags and the loop counters.
The same grid. Full copy.0.002 s, and about 0.3 MB for the copy of the grid. Markers in the grid do the same job with no copy.