LeetCode73-矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
示例 1:
输入:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
输出:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
示例 2:
输入:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
输出:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
进阶:
- 一个直接的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。
- 一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
- 你能想出一个常数空间的解决方案吗?
一、思路
这里,我们只讨论空间复杂度为常数的算法。
题目的要求很简单,但是隐藏了一些信息,假设矩阵中,有个0位于 ( i , j ) (i,j) (i,j)处,那么我们可以知道最终变换后的矩阵,第 i i i行、第 j j j列全部为0,于是有:
m a t r i x [ i ] [ 0 ] = 0 matrix[i][0]=0 matrix[i][0]=0
m a t r i x [ 0 ] [ j ] = 0 matrix[0][j]=0 matrix[0][j]=0
我们不能在找到这个0之后,马上对第 i i i行、第 j j j列进行赋值,是因为,接下来,我们需要遍历第 i + 1 i+1 i+1行(如果是按列遍历,就是第 j + 1 j+1 j+1列),如果你进行了置0的操作,那么可能会将原来第 i + 1 i+1 i+1行不是0的数变成了0,然后这个0也会被认为是原来矩阵中的0,如此一来就会出错
现在为了解决这个问题,我们对矩阵进行了划分:
这里,我们将第1行与第1列单独拿出来作为标志行与标志列,这对应于上图中蓝色的部分
这里,我们先对标致行与标志列进行遍历,看看有没有0,如果有则将对应的flag标志置为true,表示该标志行(列)需要在最后一步中全部置0。
然后对剩下的那个小矩阵进行遍历,每当找到0的时候,就将对应的标志行与标志列的值置为0:
当遍历完整个小矩阵后,标志行与标志列的填写就完成了,此时再次遍历小矩阵,将标致行中为0的那一列全部置0,标志列中为0的那一行全部置为0
最后处理标志行与标志列,根据对应的flag来确定是否需要置零
C++代码:
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
if (matrix.empty() || matrix[0].empty())
return;
// 使用第0行、第0列的对应位置来存放0,表示该行/列全部为0
bool col0_is_zeros = false, row0_is_zeros = false;
// 先遍历一遍第0行、第0列,看看里面有没有0
for (int i = 0; i < matrix.size(); i++)
if (matrix[i][0] == 0) {
col0_is_zeros = true;
break;
}
for (int j = 0; j < matrix[0].size(); j++)
if (matrix[0][j] == 0) {
row0_is_zeros = true;
break;
}
// 遍历除第0行、第0列的矩阵,查找哪些行、列出现过0,并将对应的行、列的第一个数字提前置零,作为标志
for (int i = 1; i < matrix.size(); i++)
for (int j = 1; j < matrix[i].size(); j++)
if (matrix[i][j] == 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
// 遍历除第0行、第0列的矩阵,赋值0
for (int i = 1; i < matrix.size(); i++)
for (int j = 1; j < matrix[i].size(); j++)
if (matrix[i][0] == 0 || matrix[0][j] == 0)
matrix[i][j] = 0;
// 第0行、第0列是否为0
if (row0_is_zeros)
for (int j = 0; j < matrix[0].size(); j++)
matrix[0][j] = 0;
if (col0_is_zeros)
for (int i = 0; i < matrix.size(); i++)
matrix[i][0] = 0;
return;
}
};
执行效率: