力扣73. 矩阵置零---三法解决(使用额外空间,使用两个变量,使用一个变量)

73. 矩阵置零

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

进阶:

一个直观的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个仅使用常量空间的解决方案吗?

示例 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]]
 

提示:

m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
-231 <= matrix[i][j] <= 231 - 1

题解:

方法一:使用额外数组

这是最直观的一种方法,即由于直接对数组进行遇到0就进行上下左右等赋为0操作的话可能会让原本不应为0的变为0,于是我们可以采用一个数组来存储原先数组中为0的行数和列数,然后在遍历原数组时一旦发现其的i或j对应有一个为0,就将其改为0即可。

代码:

void setZeroes(int** matrix, int matrixSize, int* matrixColSize){
    int m = matrixSize;
    int n =*matrixColSize;
    int row[m];
    int col[n];
    memset(row,0,sizeof(int)*m);
    memset(col,0,sizeof(int)*n);
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(matrix[i][j]==0)
            {
                row[i] = 1;
                col[j] = 1;//找到对应要变为0的下标i与j
            }
        }
    }
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(row[i]==1||col[j]==1)//找到了已经,所以开始遍历原数组是否有与其i或j相同的
            {
                matrix[i][j]=0;
            }
        }
    }
}

方法二:不使用额外空间,仅使用两个变量作为标记

由上述方法一可知,我们只需要一行和一列就可以标记出需要置换为0的行标与列标,所以我们可以直接用原数组的第一行和第一列即可。但是这样的话我们会发现原数组会遭到破坏,什么破坏了呢?也就是什么情况被我们舍弃了呢?即若第一行或第一列有0的情况被我们忽略了,所以我们需要设立两个变量分别储存行与列是否有为0的,然后这样就可以将第一行与第一列解放出来作为我们方法一中的两个数组使用即可,最后再根据标记变量来判断是否还要对第一行第一列置换为0.

代码:

void setZeroes(int** matrix, int matrixSize, int* matrixColSize){
    int temp1 = 1;
    int temp2 = 1;//因为第一行与第一列的元素其一个坐标已经确定好了,所以各自不需要两个i与j了,只要一个变量作为标志即可
    int m = matrixSize;
    int n = *matrixColSize;
    for(int i=0;i<m;i++)
    {
        if(matrix[i][0]==0)
        {
            temp1 = 0;
            break;
        }
    }
    for(int i=0;i<n;i++)
    {
        if(matrix[0][i]==0)
        {
            temp2 = 0;
            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(temp1==0)
    {
        for(int i=0;i<m;i++)
        {
            matrix[i][0]=0;
        }
    }
    if(temp2==0)
    {
        for(int i=0;i<n;i++)
        {
            matrix[0][i]=0;
        }
    }
}

方法三:仅使用一个变量标记

若设立一个变量来存储第一列是否有0,此时第一列解放出来当成我们的“额外数组”使用,所以我们从每一行的第二个数开始遍历(因为第一个数当做“额外数组”使用了),一旦遇到0便把他们的对应的matrix[0][j]与matrix[i][0]置换为0.(这里因为他们本来由于对应关系就要变为0,我们只是先把他们提前变好以便作为标志量使用而已)。

遍历完全后我们会发现由于我们只设置了一个变量存储第一列是否有0,而第一行是否有0我们是没有储存的,但是我们发现我们在进行“填充”操作时,即寻找i为什么数,j为什么数要置换为0时,遍历了第一行,也就是如果第一行有0,我们已经将对应的列元素matrix[0][0]置换为0了,这样的话最终从下到上重新遍历数组置换为0时由于matrix[0][0]会作为我们漏掉的第一行的标识元素来帮助我们标记这一行是否有0,所以一个变量即可完成操作。

为什么最后一步置换0要从下到上遍历呢
如果我们从第一行开始,由于第一行并不是像方法二中那样提前被我们用一个变量来存储其中的为0情况,且他本身要作为特征元素对下面的元素进行置换,所以他本身要尽可能的晚修改
并且第一行如果过早的置换为0我们会发现会导致许多无关0的出现。因为第一行是作为特征元素出现。

因此实际上,我们无需单独使用变量记录第一行是否有0,因为如果第一行有0,那么在第一次遍历中就将matrix【0】【0】置为0,在第二次逆向遍历的最后执行到i=0时,if中的或条件会判断到matrix【0】【0】为0,进而会对所有第一行更新为0。

代码:

void setZeroes(int** matrix, int matrixSize, int* matrixColSize) {
    int m = matrixSize;
    int n = matrixColSize[0];
    int flag_col0 = false;//明确我们构建变量的目的是为了看第一行和第一列是否有0
    for (int i = 0; i < m; i++) 
    {
        if (!matrix[i][0]) {
            flag_col0 = true;//因为把两个步骤合一块了,但是还是要把应该先处理的放前面
        }
        for (int j = 1; j < n; j++) {
            if (!matrix[i][j]) {
                matrix[i][0] = matrix[0][j] = 0;
            }
        }
    }
    for (int i = m - 1; i >= 0; i--) 
    {//为了防止每一列的第一个元素被提前更新,我们需要从最后一行开始,倒序地处理矩阵元素
        for (int j = 1; j < n; j++) {
            if (!matrix[i][0] || !matrix[0][j]) {
                matrix[i][j] = 0;//不符合不会到
            }
        }
        if (flag_col0) {
            matrix[i][0] = 0;//把两个步骤放一块可以省去一些代码,也遵循原先的先后顺序
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向光.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值