LeetCode 73中等矩阵 · 标记
矩阵置零 图解题解
这道题到底在问什么
矩阵中某个元素为 0,就把它所在行和列全部置 0,要求原地。
- matrix
- [[1,1,1],[1,0,1],[1,1,1]]
- 输出
- 中间行和中间列置 0
最优解:一步一步想明白
- 3关键不是背模板,而是看懂为什么这题适合矩阵 · 标记。
- 4要把含 0 的行列整体清零,又想 O(1) 空间。诀窍:拿第一行、第一列当备忘录,记下哪些行/列要清零,省掉额外数组。先看到内部 (1,1)=0。
- 5内部格 (1,1)=0 → 在它的行头 (1,0) 和列头 (0,1) 各写一个 0 当标记。注意:这一遍只标记、不清零,免得新 0 干扰后面扫描。
- 6第二遍扫内部:只要某格的行头或列头是 0,就把它清零 → (1,2)、(2,1) 变 0。而 (2,2) 的行头 (2,0)、列头 (0,2) 都不是 0,所以保留。
- 7首行/首列被复用成了标记位,得用两个布尔变量提前记它们原本有没有 0。本例原首行、原首列都没 0 → 不整体清零,标记留下的 0 正好就是答案。
- 8结果:[[1,0,1],[0,0,0],[1,0,1]]。全程没开额外矩阵/数组,只借首行首列 → 空间 O(1)。
- 11把这句话记住,下次遇到同类题,就能更快选出方向。
⚠️ 容易写错的地方
✗ 错:一看到 0 就立刻清整行整列
✓ 对:先标记,后清零
立刻清会制造新的 0 干扰扫描
✗ 错:忘了首行/列自身原本是否含 0
✓ 对:先用两个布尔变量单独记下来
首行列被复用成标记位后,它们自己该不该清零的信息会被覆盖,必须提前记。
✗ 错:变量名和动画不一致
✓ 对:代码变量沿用动画里的核心名字
学习时最怕脑内维护两套概念
完整代码(Python / C++ / Java)
Python
class Solution:
def setZeroes(self, matrix):
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] = matrix[0][j] = 0
for i in range(1, m):
for j in range(1, n):
if matrix[i][0] == 0 or matrix[0][j] == 0:
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] = 0C++
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] = 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 (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;
}
};Java
class Solution {
public void setZeroes(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
boolean 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] = 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 (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;
}
}套路模板 · 矩阵 · 标记
# 矩阵置零骨架 · 首行首列当标记
row0 = 第一行是否含 0; col0 = 第一列是否含 0
for i in 1..m-1, j in 1..n-1: # 第一遍:只标记
if a[i][j]==0: a[i][0]=a[0][j]=0
for i in 1..m-1, j in 1..n-1: # 第二遍:按标记清零
if a[i][0]==0 or a[0][j]==0: a[i][j]=0
if row0: 清空第一行
if col0: 清空第一列复杂度
时间复杂度
O(mn)
两遍扫整个矩阵,约 2mn 次访问
空间复杂度
O(1)
只借首行首列 + 两个布尔变量,不开额外矩阵/数组
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 矩阵置零 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么用第一行/列当标记位?+
它们本就要根据其它格清零,正好复用来记「这一行/列是否含 0」,省额外数组。
第一行/列自身怎么不被污染?+
用两个独立布尔变量先记第一行、第一列原本是否含 0,最后再据此处理它们。
复杂度?+
O(m·n) 时间、O(1) 额外空间。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。