题目源自于Leetcode和Cracking Coding Interview。
题目:Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.
思路:如果直接遍历整个矩阵,遇到0就把当前行当前列设为全0,那么最终会错误地将整个矩阵全变为0。所以要先过一遍,记住哪一行应该设为全0,那一列应该设为全0,然后第二次的时候再设0。
关键点在辅助空间消耗上。最笨的办法是完全复制一个新的矩阵,在新的矩阵上设0,这样就不会出现刚才说的错误,可能遍历一遍搞定,空间开销是O(m*n)。好些的办法是申请一个行数组和一个列数组,分别记录每行、每列是否应该设为0, 空间开销是O(m + n)。题目提示出,能否让空间开销为O(1)。
我想出了两个办法。
第一个办法是,遍历时遇到0,就把当前行当前列的非0的数全变为一个特殊的数(比如INT_MAX)。第二遍遍历时,把所有的特殊的数修改为0。不过,在提交答案的时候,测试数据矩阵中就含有我设置的特殊的数,没有别的特殊的数了,所以这个方法没走通。
第二个方法是,选取矩阵的第一行、第一列,作为刚才O(m + n)那个方法中的行数组和列数组,用来做记录。所以我得把他们单独拿出来,先处理这一行和这一列。
代码:
class Solution {
public:
void setZeroes(vector<vector<int> > &matrix) {
int m = matrix.size();
if(m<=0)
return;
int n = matrix[0].size();
if(n<=0)
return;
int i,j;
int r0, c0;
r0 = c0 = 0;
for(i=0;i<m;i++) //先记住第一列最终是否要设置为全0
if(matrix[i][0] == 0)
{
c0 = 1;
break;
}
for(j=0;j<n;j++) //先记住第一行最终是否要设置为全0
if(matrix[0][j] == 0)
{
r0 = 1;
break;
}
for(i=0;i<m;i++) //第一遍全体遍历,做记录
for(j=0;j<n;j++)
{
if(matrix[i][j] == 0)
{
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
for(i=1;i<m;i++) //根据第一列的记录情况,设0
if(matrix[i][0] == 0)
{
for(j=1;j<n;j++)
matrix[i][j] = 0;
}
for(j=1;j<n;j++) //根据第一行的记录情况,设0
if(matrix[0][j] == 0)
{
for(i=1;i<m;i++)
matrix[i][j] = 0;
}
if(c0 == 1) //第一列设置为全0
{
for(i=0;i<m;i++)
matrix[i][0] = 0;
}
if(r0 == 1) //第一行设置为全0
{
for(j=0;j<n;j++)
matrix[0][j] = 0;
}
}
};