1. 问题背景与核心挑战
矩阵置零是算法面试中的经典问题,题目要求给定一个m×n的矩阵,如果某个元素为0,则将其所在行和列的所有元素都设为0。这个问题看似简单,但考察的是对空间复杂度的优化能力。
我第一次遇到这个问题是在2020年参加某大厂面试时,面试官给出了一个看似直白的解法后,突然追问:"如果要求O(1)空间复杂度,你会怎么做?"当时我意识到,这道题远没有表面看起来那么简单。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 暴力解法与空间复杂度分析
最直观的解法是使用额外的标记数组记录需要置零的行和列:
python复制def setZeroes(matrix):
m, n = len(matrix), len(matrix[0])
rows, cols = [False]*m, [False]*n
# 第一次遍历:标记需要置零的行列
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
rows[i] = True
cols[j] = True
# 第二次遍历:执行置零操作
for i in range(m):
for j in range(n):
if rows[i] or cols[j]:
matrix[i][j] = 0
这个解法的时间复杂度是O(mn),空间复杂度是O(m+n)。虽然能通过测试,但面试官通常期望更优的解法。
3. O(1)空间复杂度优化思路
要实现O(1)空间复杂度,我们需要利用矩阵本身来存储标记信息。关键思路是:
- 使用第一行和第一列作为标记位
- 先处理第一行和第一列是否需要置零
- 用第一行标记各列是否需要置零
- 用第一列标记各行是否需要置零
具体实现时需要注意边界条件,特别是第一行和第一列本身包含0的情况。
4. 完整优化解法实现
python复制def setZeroes(matrix):
m, n = len(matrix), len(matrix[0])
first_row_has_zero = any(matrix[0][j] == 0 for j in range(n))
first_col_has_zero = 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 first_row_has_zero:
for j in range(n):
matrix[0][j] = 0
if first_col_has_zero:
for i in range(m):
matrix[i][0] = 0
这个解法的时间复杂度仍然是O(mn),但空间复杂度优化到了O(1)。
5. 边界条件与测试用例设计
在实现过程中,有几个关键边界条件需要注意:
- 矩阵为空的情况
- 只有一行或一列的情况
- 第一行或第一列本身包含0的情况
- 矩阵中所有元素都为0的情况
好的测试用例应该覆盖这些边界条件:
python复制test_cases = [
([[1,1,1],[1,0,1],[1,1,1]], [[1,0,1],[0,0,0],[1,0,1]]),
([[0,1,2,0],[3,4,5,2],[1,3,1,5]], [[0,0,0,0],[0,4,5,0],[0,3,1,0]]),
([[1]], [[1]]),
([[0]], [[0]]),
([], []),
([[1,0]], [[0,0]])
]
6. 常见错误与调试技巧
在实现这个算法时,开发者常犯的错误包括:
- 忘记单独处理第一行和第一列
- 在标记阶段就修改了第一行和第一列的值
- 循环边界条件设置错误
调试时可以:
- 打印中间状态矩阵
- 使用小规模矩阵手动模拟
- 特别注意第一行和第一列的变化
7. 算法扩展与变种问题
这个问题的变种包括:
- 只将0所在的行置零,不处理列
- 将0所在的行和列置为1而不是0
- 使用不同的标记值(如-1)表示需要置零的位置
对于这些变种,核心思路类似,但需要调整标记和置零的逻辑。
8. 实际应用场景
矩阵置零算法在实际中有多种应用:
- 图像处理中的遮罩操作
- 电子表格软件中的条件格式化
- 游戏开发中的地图初始化
- 科学计算中的数据清洗
理解这个算法有助于处理类似的网格状数据结构问题。
9. 性能优化与进阶思考
对于特别大的矩阵,可以考虑:
- 分块处理以减少缓存未命中
- 并行化处理行和列的置零操作
- 使用位运算进一步压缩标记空间
在面试中,讨论这些优化方向可以展示更深入的计算机系统知识。
10. 与其他LeetCode题目的关联
矩阵置零与以下题目有相似之处:
- 289.生命游戏 - 同样需要原地修改矩阵
- 54.螺旋矩阵 - 矩阵遍历技巧
- 48.旋转图像 - 矩阵变换操作
掌握这类问题的通用解法有助于提高解题效率。
