🔥个人主页: Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
题目解析
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。要求使用原地算法,即空间复杂度为 O (1)。
直接的思路是先遍历矩阵,记录所有 0 所在的行和列,然后再根据记录去置零。但这样会使用 O (m+n) 的额外空间。要实现 O (1) 空间,我们需要利用矩阵的第一行和第一列来存储这些标记信息。
核心思路
- 标记首行首列 :首先检查矩阵的第一行和第一列,看它们本身是否包含 0,并用两个布尔变量
row0和col0记录下来。 - 利用首行首列做标记 :遍历矩阵中除第一行和第一列的其他元素。如果发现
matrix[i][j] == 0,就将其对应的行标记matrix[i][0]和列标记matrix[0][j]置为 0。 - 根据标记置零:再次遍历矩阵中除第一行和第一列的元素,如果其对应的行标记或列标记为 0,则将该元素置零。
- 处理首行首列 :最后根据最开始记录的
row0和col0,决定是否将第一行和第一列整体置零。
完整代码
cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
bool row0 = false, col0 = false;
// 检查第一行是否有0
for (int j = 0; j < n; ++j) {
if (matrix[0][j] == 0) {
row0 = true;
break;
}
}
// 检查第一列是否有0
for (int i = 0; i < m; ++i) {
if (matrix[i][0] == 0) {
col0 = true;
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;
}
}
}
// 如果首行原本有0,将首行置零
if (row0) {
for (int j = 0; j < n; ++j) {
matrix[0][j] = 0;
}
}
// 如果首列原本有0,将首列置零
if (col0) {
for (int i = 0; i < m; ++i) {
matrix[i][0] = 0;
}
}
}
};
复杂度分析
- 时间复杂度 :O(m*n)
- 我们对矩阵进行了多次线性遍历,总的时间复杂度与矩阵元素的数量成正比。
- 空间复杂度 :O(1)
- 我们只使用了两个额外的布尔变量,满足了原地算法的要求。
示例演示
以 matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]] 为例:
- 检查首行首列:首行有 0(
row0 = true),首列有 0(col0 = true)。 - 标记:遍历其他元素,发现
matrix[0][0]和matrix[0][3]已为 0,无需额外标记。 - 置零:根据标记,将除首行首列外的元素置零,得到
[[0,1,2,0],[0,4,5,0],[0,3,1,0]]。 - 处理首行首列:因为
row0和col0都为 true,所以将首行和首列全部置零,得到最终结果[[0,0,0,0],[0,4,5,0],[0,3,1,0]]。