#73 矩阵操作 中等

矩阵置零

在 LeetCode 上查看 ↗

🎧 语音讲解

开车或通勤时可听,跟着思路走一遍

速度

📋 题目描述

给定一个 m × n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

1 <= m, n <= 200,元素取值在 32 位有符号整数范围内。

示例 1

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
中间 0 使第 2 行、第 2 列全部置零。

示例 2

输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
首行、末列原本就有 0,扩散后整行整列变零。

💭 模拟答题者思考

1. 暴力:先扫一遍把所有该清零的行列坐标放进两个集合,第二遍按集合改矩阵——正确,但需要 O(m+n) 额外空间,进阶要求还要压到 O(1)。

2. 重复在哪里?每个 0 都在重复声明「我这一行、这一列都要变零」——信息可以压缩成「哪些行、哪些列需要清零」两个 bitmask;在 m,n≤200 时甚至可以用整型位集,但题目更经典的写法是复用矩阵 的第一行和第一列当标记数组。

3. 陷阱:若第一行/第一列本身就有 0,它们的标记格会被提前污染。做法是先单独用 row0、col0 记录首行首列是否该清零,内层只从 (1,1) 扫描,用 matrix[i][0]、matrix[0][j] 记录第 i 行、第 j 列是否要清零。

4. 手推示例 1:仅 matrix[1][1]=0,标记后 matrix[1][0]=0、matrix[0][1]=0;按行标记清第 2 行,按列标记清第 2 列,首行首列无 row0/col0,得到目标矩阵。

5. 三阶段顺序不能乱:① 记录 row0/col0 → ② 内层扫 0 写标记 → ③ 按标记清内部 → ④ 最后处理首行首列。遍历时若边扫边立刻清零,会把尚未扫描到的真实 0「抹掉」,漏标行列。

🧠 变量语义(先读这三句再编码)

变量类型语义(三句法)
matrix[i][0]int(复用)定义:第 i 行最左格,扫描阶段同时是真实元素;标记阶段表示「第 i 行是否需要整行置零」
维护:内层扫描发现 matrix[i][j]==0 时置 matrix[i][0]=0(i≥1)
更新:第二遍若 matrix[i][0]==0 则将该行 j=1..n-1 清零;最后若 col0 再清整列 0
matrix[0][j]int(复用)定义:第 j 列最上格,扫描阶段是真实元素;标记阶段表示「第 j 列是否需要整列置零」
维护:内层发现 matrix[i][j]==0 时置 matrix[0][j]=0(j≥1)
更新:第二遍若 matrix[0][j]==0 则将该列 i=1..m-1 清零;最后若 row0 再清整行 0
row0bool定义:第一行在原始矩阵中是否曾出现 0
维护:首行不能单靠 matrix[0][j] 当标记(会与列标记互相覆盖),故用独立布尔量
更新:预处理扫 matrix[0][j] 得到;所有内层操作完成后,若真则把第一行全部置 0
col0bool定义:第一列在原始矩阵中是否曾出现 0
维护:与 row0 对称,保护第一列不被 matrix[i][0] 行标记误伤
更新:预处理扫 matrix[i][0] 得到;最后若真则把第一列全部置 0
i, jint定义:矩阵下标,m=len(matrix),n=len(matrix[0])
维护:标记扫描只走 i=1..m-1, j=1..n-1,避免用尚未稳定的首行首列当依据
更新:双重循环递增,驱动「找零 → 打标记 → 按标记清零」三阶段

⌨️ 落码步骤

1. m, n = len(matrix), len(matrix[0]);扫描第一行得 row0,扫描第一列得 col0

2. 对 i=1..m-1, j=1..n-1:若 matrix[i][j]==0,令 matrix[i][0]=0 且 matrix[0][j]=0

3. 对 i=1..m-1:若 matrix[i][0]==0,将该行 j=1..n-1 全部置 0

4. 对 j=1..n-1:若 matrix[0][j]==0,将该列 i=1..m-1 全部置 0

5. 若 row0 则清零第一行;若 col0 则清零第一列(原地修改,无返回值)

💻 代码实现

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        m, n = len(matrix), len(matrix[0])
        row0 = any(matrix[0][j] == 0 for j in range(n))
        col0 = any(matrix[i][0] == 0 for i in range(m))
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == 0:
                    matrix[i][0] = 0
                    matrix[0][j] = 0
        for i in range(1, m):
            if matrix[i][0] == 0:
                for j in range(1, n):
                    matrix[i][j] = 0
        for j in range(1, n):
            if matrix[0][j] == 0:
                for i in range(1, m):
                    matrix[i][j] = 0
        if row0:
            for j in range(n):
                matrix[0][j] = 0
        if col0:
            for i in range(m):
                matrix[i][0] = 0
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        bool row0 = false, col0 = false;
        for (int j = 0; j < n; ++j)
            if (matrix[0][j] == 0) row0 = true;
        for (int i = 0; i < m; ++i)
            if (matrix[i][0] == 0) col0 = true;
        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) {
            if (matrix[i][0] == 0) {
                for (int j = 1; j < n; ++j) matrix[i][j] = 0;
            }
        }
        for (int j = 1; j < n; ++j) {
            if (matrix[0][j] == 0) {
                for (int i = 1; i < m; ++i) matrix[i][j] = 0;
            }
        }
        if (row0) {
            for (int j = 0; j < n; ++j) matrix[0][j] = 0;
        }
        if (col0) {
            for (int i = 0; i < m; ++i) matrix[i][0] = 0;
        }
    }
};
// 时间 O(m×n),空间 O(1)

📈 复杂度分析

时间复杂度 O(m × n)
空间复杂度 O(1)(不计输入)

⚠️ 常见坑

❗ 边扫描边立即把整行整列清零:会把后面的真实 0 提前抹掉,漏标应清零的行列。

❗ 忘记单独处理第一行/第一列:直接用 matrix[0][j]、matrix[i][0] 记录首行首列是否该清零,会与标记阶段互相覆盖,必须用 row0 / col0。

❗ 标记与清零顺序颠倒:须先完成全部「写标记」,再按标记批量清零,最后才处理 row0/col0 对应的首行首列。

🔍 必测边界 Case

Case 1:单格矩阵
matrix = [[0]] → [[0]](row0、col0 均为真,结果仍为 0)
Case 2:无零元素
matrix = [[1,2],[3,4]] → 不变(标记阶段不写任何 0)
Case 3:首行含零
matrix = [[0,1,2,0],...] → 首行全 0(官方示例 2,依赖 row0)
Case 4:首列含零
某行第一列为 0 → 整列清零(依赖 col0 与 matrix[i][0] 标记配合)
Case 5:中心单点零
[[1,1,1],[1,0,1],[1,1,1]] → 第 2 行第 2 列全 0(官方示例 1)