73. 矩阵置零(中等)
题目
给定一个 m 行 n 列的整数矩阵。若某个位置的值是 0,就把它所在的整行、整列都改成 0;所有 0 同时生效,最终要求原地修改,不返回新矩阵。
换句话说:先找出原矩阵里所有 0,再把这些 0 所在的行、列统一清零;不能边找边清,因为新产生的 0 会继续扩散。
自造示例:[[3,1,2,4],[5,0,7,8],[1,1,1,1],[9,3,1,6]] → [[3,0,2,4],[0,0,0,0],[1,0,1,1],[9,0,1,6]]
约束:1 ≤ m, n ≤ 200;元素为 32 位整数,范围 -231 ~ 231 - 1;进阶要求额外空间 O(1)。
思路
一句话:把“哪些行、哪些列要清零”记下来,再统一清零;最高明的记法,是把标记写进矩阵自己的第 0 行和第 0 列。
最直白的做法:复制一张同大的标记矩阵,第一趟标出哪些格子原本是 0,第二趟按标记把对应行列清零。时间 O(mn),但要额外开整张矩阵,空间 O(mn),浪费。
改进一:不用整张标记矩阵,只记“哪一行、哪一列需要清”,开两个数组 row[m]、col[n]。第一趟扫到 matrix[i][j] == 0 就把 row[i]、col[j] 置真;第二趟按数组统一清零。空间降到 O(m + n),但提示说还可以更好。
关键观察:row、col 这两个数组加起来正好是 m + n 个格子,而矩阵自己的第 0 行、第 0 列恰好也有 m + n 个格子。把它们拿来当标记数组用:内部某格 (i, j) 为 0,就写 matrix[i][0] = 0、matrix[0][j] = 0。这样额外空间从 O(m + n) 降到 O(1)。
代价是必须先花两个布尔变量,单独记住第 0 行、第 0 列本身是否含 0——否则它们一旦被当成标记,原始信息就丢了。同时必须“先标记、后清零”两趟完成,边扫边清会让新产生的 0 像传染一样扩散。
解法
四步,全程原地:
- 扫描第 0 行、第 0 列,分别记下
firstRowZero、firstColZero两个布尔。 - 扫描内部区域(
i ≥ 1, j ≥ 1),遇 0 就把matrix[i][0]和matrix[0][j]置 0,作为标记。 - 再扫内部区域:
matrix[i][0] == 0或matrix[0][j] == 0时,把matrix[i][j]清零。 - 最后按两个布尔,把第 0 行 / 第 0 列整体清零(若需要)。
以示例 [[3,1,2,4],[5,0,7,8],[1,1,1,1],[9,3,1,6]](0 在 (1,1))走一遍:
| 阶段 | 矩阵快照 | 说明 |
|---|---|---|
| 标记前 | [[3,1,2,4],[5,0,7,8],[1,1,1,1],[9,3,1,6]] | 第 0 行 / 列本身无 0,两布尔都是 false |
| 第 2 步后 | [[3,0,2,4],[0,0,7,8],[1,1,1,1],[9,3,1,6]] | matrix[0][1]、matrix[1][0] 被置 0,代表“第 1 行、第 1 列要清” |
| 第 3 步后 | [[3,0,2,4],[0,0,0,0],[1,0,1,1],[9,0,1,6]] | 内部按标记清完,第 0 行 / 列不受影响,结果正确 |
下图把上面 4 个步骤串成流程,并逐步演示:


C++
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
bool firstRowZero = false, firstColZero = false; // 先单独记下第 0 行 / 第 0 列本身有没有 0
for (int j = 0; j < n; ++j) {
if (matrix[0][j] == 0) firstRowZero = true;
}
for (int i = 0; i < m;本篇为付费内容
您已阅读试读部分,购买后即可解锁全文。
⚠️ 本站内容已做唯一性标记并受版权保护,未经授权禁止转载,侵权必究。
还没有评论,来说两句?