题目描述
给定一个 m x n
的矩阵 matrix
,其中每个元素不是 0 就是非零整数。
如果一个元素是 0,则将它所在的整行和整列都置为 0。请你实现这个操作,要求 空间复杂度 O(1)。
示例
示例 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]
]
解题思路
1. 暴力法
- 最直观的做法是,遍历整个矩阵,遇到 0 时,标记该位置所在的行和列,并将这些行和列的所有元素设置为 0。
- 这种方法的空间复杂度是 O(m + n),因为我们需要额外的数组来记录需要置零的行和列。
2. 空间优化(O(1) 空间复杂度)
- 题目要求空间复杂度是 O(1),即不能使用额外的数组(如布尔数组)来存储行和列的状态。我们可以在原地进行修改。
- 通过使用矩阵的 第一行 和 第一列 来记录该行和该列是否需要置零。
- 第一行 和 第一列 本身也可能包含零,所以需要额外的变量来记录它们是否需要置零。
3. 优化步骤:
- 检查第一行和第一列:我们需要一个标志来记录第一行和第一列是否应该被置零。
- 遍历矩阵:从矩阵的第二行和第二列开始,如果
matrix[i][j] == 0
,就将matrix[i][0]
和matrix[0][j]
设置为 0,表示该行和列应该被置零。 - 根据标志置零:根据第一行和第一列的标记,更新矩阵中的其它元素。
- 处理第一行和第一列:最后根据记录的标志更新第一行和第一列。
实现代码:
#include <stdio.h>
void setZeroes(int** matrix, int matrixSize, int* matrixColSize) {
int m = matrixSize; // 矩阵的行数
int n = *matrixColSize; // 矩阵的列数
int firstRowZero = 0, firstColZero = 0;
// 检查第一行是否需要置零
for (int j = 0; j < n; j++) {
if (matrix[0][j] == 0) {
firstRowZero = 1;
break;
}
}
// 检查第一列是否需要置零
for (int i = 0; i < m; i++) {
if (matrix[i][0] == 0) {
firstColZero = 1;
break;
}
}
// 使用第一行和第一列标记其他行列需要置零
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++) {
for (int j = 1; j < n; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}
// 处理第一行
if (firstRowZero) {
for (int j = 0; j < n; j++) {
matrix[0][j] = 0;
}
}
// 处理第一列
if (firstColZero) {
for (int i = 0; i < m; i++) {
matrix[i][0] = 0;
}
}
}
int main() {
int m = 3, n = 4;
// 动态分配内存
int** matrix = (int**)malloc(m * sizeof(int*));
for (int i = 0; i < m; i++) {
matrix[i] = (int*)malloc(n * sizeof(int));
}
// 初始化矩阵
int matrixData[3][4] = {
{0, 1, 2, 0},
{3, 4, 5, 2},
{1, 3, 1, 5}
};
// 将数据复制到动态分配的矩阵中
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
matrix[i][j] = matrixData[i][j];
}
}
setZeroes(matrix, m, &n);
// 打印结果
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 释放内存
for (int i = 0; i < m; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
步骤说明:
- 检查第一行和第一列是否需要置零:
- 使用变量
firstRowZero
和firstColZero
来分别标记第一行和第一列是否包含零。
- 使用变量
- 遍历矩阵并标记需要置零的行和列:
- 从第二行第二列开始,遍历矩阵。如果遇到
matrix[i][j] == 0
,就将matrix[i][0]
和matrix[0][j]
设置为零,表示第i
行和第j
列需要置零。
- 从第二行第二列开始,遍历矩阵。如果遇到
- 根据标记的行列更新矩阵:
- 再次遍历矩阵,按照第一行和第一列的标记将相应位置的元素置零。
- 处理第一行和第一列:
- 如果
firstRowZero
为 1,则将第一行所有元素置零。 - 如果
firstColZero
为 1,则将第一列所有元素置零。
- 如果
时间复杂度与空间复杂度
时间复杂度:
- 遍历矩阵两次,因此时间复杂度是 O(m * n),其中
m
和n
分别是矩阵的行数和列数。
空间复杂度:
- 我们仅使用了常量的额外空间(
firstRowZero
和firstColZero
),因此空间复杂度是 O(1),符合题目要求。