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

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
示例 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的坐标,再将对应坐标的行和列都设置为0
进阶做法:原地算法
方法思路
- 标记首行和首列:检查矩阵的第一行和第一列是否原本存在 0,用布尔变量记录。
- 利用首行首列标记:遍历矩阵其余元素,若
matrix[i][j] == 0,则将matrix[i][0]和matrix[0][j]置为 0,利用首行首列作为标记数组。 - 根据标记置零:遍历首行首列以外的元素,若
matrix[i][0] == 0或matrix[0][j] == 0,则将matrix[i][j]置为 0。 - 处理首行首列:根据步骤 1 的标记,决定是否将首行或首列全部置零。
代码实现
function setZeroes(matrix) {
const m = matrix.length;
if (m === 0) return;
const n = matrix[0].length;
// 标记首行和首列是否原本存在 0
let firstRowHasZero = false;
let firstColHasZero = false;
// 检查首行
for (let j = 0; j < n; j++) {
if (matrix[0][j] === 0) {
firstRowHasZero = true;
break;
}
}
// 检查首列
for (let i = 0; i < m; i++) {
if (matrix[i][0] === 0) {
firstColHasZero = true;
break;
}
}
// 利用首行首列标记其余行和列
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
if (matrix[i][j] === 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
// 根据标记置零(不处理首行首列)
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
if (matrix[i][0] === 0 || matrix[0][j] === 0) {
matrix[i][j] = 0;
}
}
}
// 处理首行
if (firstRowHasZero) {
for (let j = 0; j < n; j++) {
matrix[0][j] = 0;
}
}
// 处理首列
if (firstColHasZero) {
for (let i = 0; i < m; i++) {
matrix[i][0] = 0;
}
}
}
复杂度分析
- 时间复杂度:O(m×n),需遍历矩阵三次。
- 空间复杂度:O(1),仅使用常数级额外空间。
示例分析
输入矩阵:
[
[1, 1, 1],
[1, 0, 1],
[1, 1, 1]
]
- 标记首行首列:首行和首列均无 0,标记为
false。 - 利用首行首列标记:发现
matrix[1][1] == 0,将matrix[1][0]和matrix[0][1]置为 0:[
[1, 0, 1],
[0, 0, 1],
[1, 1, 1]
] - 根据标记置零:将
matrix[1][0] == 0和matrix[0][1] == 0对应的元素置零:[
[1, 0, 1],
[0, 0, 0],
[1, 0, 1]
] - 处理首行首列:首行首列无需置零,最终结果:
[
[1, 0, 1],
[0, 0, 0],
[1, 0, 1]
]
正确性验证
- 覆盖所有情况:通过首行首列的标记,确保所有原 0 所在的行列被正确置零。
- 避免误操作:先检查首行首列是否原本存在 0,避免因后续标记导致误判。
此方法通过巧妙利用矩阵自身的首行首列作为标记数组,在不使用额外空间的情况下完成任务,是最优解。