主题
本文从
规则
对于
方法一:使用额外数组
用一个长为
- 遍历
,如果包含 ,那么置 。
用一个长为
- 遍历
的 列,如果包含 ,那么置 。
然后,再次遍历
答疑
问:能不能在发现
答:这样做是错误的。如示例 1,如果直接全变成

python
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
row_has_zero = [0 in row for row in matrix] # 行是否包含 0
col_has_zero = [0 in col for col in zip(*matrix)] # 列是否包含 0
for i, row0 in enumerate(row_has_zero):
for j, col0 in enumerate(col_has_zero):
if row0 or col0: # i 行或 j 列有 0
matrix[i][j] = 0 # 题目要求原地修改,无返回值cpp
// C++ 版待补充cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<int8_t> row_has_zero(m); // 行是否包含 0
vector<int8_t> col_has_zero(n); // 列是否包含 0
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 0) {
row_has_zero[i] = col_has_zero[j] = true;
}
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (row_has_zero[i] || col_has_zero[j]) { // i 行或 j 列有 0
matrix[i][j] = 0; // 题目要求原地修改,无返回值
}
}
}
}
};复杂度分析
- 时间复杂度:
,其中 和 分别是 的行数和列数。 - 空间复杂度:
。
方法二:不使用额外数组
回想一下 Excel 表格,通常用第一行和第一列保存汇总信息。
对于本题,能否把数据保存到
用第一列的
用第一行的
然后,再次遍历
看上去,问题解决了?
然而,如果第一行全为
解决办法:在一开始,额外用两个布尔变量分别记录第一行是否包含
既然最后会单独修改第一行和第一列,那么在修改
写法一
python
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
m, n = len(matrix), len(matrix[0])
first_row_has_zero = 0 in matrix[0] # 记录第一行是否包含 0
first_col_has_zero = any(row[0] == 0 for row in matrix) # 记录第一列是否包含 0
# 用第一列 matrix[i][0] 保存 row_has_zero[i]
# 用第一行 matrix[0][j] 保存 col_has_zero[j]
for i in range(1, m): # 无需遍历第一行,如果 matrix[0][j] 本身是 0,那么相当于 col_has_zero[j] 已经是 True
for j in range(1, n): # 无需遍历第一列,如果 matrix[i][0] 本身是 0,那么相当于 row_has_zero[i] 已经是 True
if matrix[i][j] == 0:
matrix[i][0] = 0 # 相当于 row_has_zero[i] = True
matrix[0][j] = 0 # 相当于 col_has_zero[j] = True
for i in range(1, m): # 跳过第一行,留到最后修改
for j in range(1, n): # 跳过第一列,留到最后修改
if matrix[i][0] == 0 or matrix[0][j] == 0: # i 行或 j 列有 0
matrix[i][j] = 0
# 如果第一列一开始就包含 0,那么把第一列全变成 0
if first_col_has_zero:
for row in matrix:
row[0] = 0
# 如果第一行一开始就包含 0,那么把第一行全变成 0
if first_row_has_zero:
for j in range(n):
matrix[0][j] = 0cpp
// C++ 版待补充cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
bool first_row_has_zero = ranges::contains(matrix[0], 0); // 记录第一行是否包含 0
bool first_col_has_zero = ranges::any_of(matrix, [](auto& row) { return row[0] == 0; }); // 记录第一列是否包含 0
// 用第一列 matrix[i][0] 保存 row_has_zero[i]
// 用第一行 matrix[0][j] 保存 col_has_zero[j]
for (int i = 1; i < m; i++) { // 无需遍历第一行,如果 matrix[0][j] 本身是 0,那么相当于 col_has_zero[j] 已经是 true
for (int j = 1; j < n; j++) { // 无需遍历第一列,如果 matrix[i][0] 本身是 0,那么相当于 row_has_zero[i] 已经是 true
if (matrix[i][j] == 0) {
matrix[i][0] = 0; // 相当于 row_has_zero[i] = true
matrix[0][j] = 0; // 相当于 col_has_zero[j] = true
}
}
}
for (int i = 1; i < m; i++) { // 跳过第一行,留到最后修改
for (int j = 1; j < n; j++) { // 跳过第一列,留到最后修改
if (matrix[i][0] == 0 || matrix[0][j] == 0) { // i 行或 j 列有 0
matrix[i][j] = 0;
}
}
}
// 如果第一列一开始就包含 0,那么把第一列全变成 0
if (first_col_has_zero) {
for (auto& row : matrix) {
row[0] = 0;
}
}
// 如果第一行一开始就包含 0,那么把第一行全变成 0
if (first_row_has_zero) {
ranges::fill(matrix[0], 0);
}
}
};写法二
记录行列是否包含
- 如果第一列全为
,那么在遍历过程中,不会修改 ,所以遍历结束后 仍然是 。 - 如果第一列包含
,那么在遍历过程中,我们会把 置为 。
所以可以直接用
python
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
m, n = len(matrix), len(matrix[0])
first_row_has_zero = 0 in matrix[0]
for i in range(1, m):
for j in range(n): # 如果第一列包含 0,那么 matrix[0][0] 会置为 0
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
# 注意顺序,先改第一列,再改第一行(避免把 matrix[0][0] 从 1 改成 0 影响判断)
if matrix[0][0] == 0: # 替换原来的 first_col_has_zero
for row in matrix:
row[0] = 0
if first_row_has_zero:
for j in range(n):
matrix[0][j] = 0cpp
// C++ 版待补充cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
bool first_row_has_zero = ranges::contains(matrix[0], 0);
for (int i = 1; i < m; i++) {
for (int j = 0; j < n; j++) { // 如果第一列包含 0,那么 matrix[0][0] 会置为 0
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;
}
}
}
// 注意顺序,先改第一列,再改第一行(避免把 matrix[0][0] 从 1 改成 0 影响判断)
if (matrix[0][0] == 0) { // 替换原来的 first_col_has_zero
for (auto& row : matrix) {
row[0] = 0;
}
}
if (first_row_has_zero) {
ranges::fill(matrix[0], 0);
}
}
};写法三
把
对比一下,如果正着遍历
下面的代码,前两个循环可以合在一起看,我们遍历了一次
python
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
m, n = len(matrix), len(matrix[0])
first_row_has_zero = 0 in matrix[0]
for i in range(1, m):
for j in range(n):
if matrix[i][j] == 0:
matrix[i][0] = matrix[0][j] = 0
for i in range(1, m):
# 倒着遍历,避免提前把 matrix[i][0] 改成 0,误认为这一行要全部变成 0
for j in range(n - 1, -1, -1):
if matrix[i][0] == 0 or matrix[0][j] == 0:
matrix[i][j] = 0
if first_row_has_zero:
for j in range(n):
matrix[0][j] = 0cpp
// C++ 版待补充cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
bool first_row_has_zero = ranges::contains(matrix[0], 0);
for (int i = 1; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = matrix[0][j] = 0;
}
}
}
for (int i = 1; i < m; i++) {
// 倒着遍历,避免提前把 matrix[i][0] 改成 0,误认为这一行要全部变成 0
for (int j = n - 1; j >= 0; j--) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}
if (first_row_has_zero) {
ranges::fill(matrix[0], 0);
}
}
};复杂度分析
- 时间复杂度:
,其中 和 分别是 的行数和列数。 - 空间复杂度:
。
相似题目
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府