【每日一好题】这么经典的题你不能不会:矩阵置零

本文详细解析了一道编程题,题目要求使用原地算法将矩阵中0元素所在的行和列全部置零。最优解法是利用矩阵的第一行和第一列作为标记,空间复杂度降至O(1)。首先遍历矩阵,用第一行和第一列标记0的位置,然后再次遍历矩阵,根据标记将元素置零。最后,代码实现部分给出了详细的注释。
摘要由CSDN通过智能技术生成

 

大家好啊,我是不一样的烟火a,今天我要为大家分享一道好题,这道题也是一道常考题,所以大家务必掌握哦。为了避免以后忘了时再想看就找不到了,所以建议收藏。🦀最后提前祝大家国庆节快乐。


🧧一、题目描述

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

 

示例 1:

eff14a2c31004fe09361dfb1d3795be5.png

 

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]

 示例 2:

8078127584b64f92a87a33719aa3b45f.png

输入: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
  • -2^31 <= matrix[i][j] <= 2^31 - 1

 

进阶:

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

快速跳转题目:矩阵置零

🏮二、思路解析(最优解法)

思路:

  • 我们可以用两个数组分别标记矩阵里面0元素的横坐标和纵坐标。我们用两个数组的下标来代替矩阵里面0元素的下标即可,把两个数组的某个位置的元素置为0即为标记。我们遍历一遍矩阵,遇到0元素,我们就将两个数组对应下标位置设为0进行标记。

a8e5461e97cd4406aa993ad43df00f98.png

 

  • 我们将矩阵中所有0元素的横纵坐标都标记后,我们这时再遍历一遍矩阵,如果发现当前元素的横坐标或者纵坐标被标记过,那么就将当前元素设置为0。

1bf2ccb218544d129a32c2a220105e3b.png

 

  • 但是我们如果单独创建2个数组来标记0元素的横纵坐标的话,空间复杂度就为O(m + n) 了,所以我们可以直接将矩阵的第一行用来标记0元素的横坐标,用矩阵的第一列来标记0元素的纵坐标。这样就可以将空间复杂度降为O(1)了。但是我们这时又需要考虑第一行或者第一列是否本身就存在0元素,如果第一行或第一列本身就存在0元素,我们就要将第一列或第一行的所有元素置为0,所以我们需要设置2个flag来标记一下第一行和第一列是否本身就存在0元素。如果我们再想一想,就可以发现,我们其实只用设置一个flag来标记第一列是否本身就存在0元素即可,因为第一列的第一个元素就可以当flag用,如果第一行本身存在0元素,我们在遍历第一行的时候就会将第一列第一个元素标记为0,这样就节省了一个变量的大小。

解题步骤:

1.初始状态:由于第一列中本身就存在0元素,所以我们将flag设置为true,到最后我们就会将第一列所有元素置成0。

a8e54e3f8c2045189190a4a018e5001f.png

 

2.我们用第一列和第一行来标记0元素的横纵坐标。

8b7cd46e3c9f4a36a1519f0acbf5cd5e.png

 

3.我们从最后一行,每行的第二列开始遍历矩阵,只要当前元素的横纵坐标有一个被标记,我们就将当前元素置为0。(为什么要从最后一行开始遍历:我们遍历的时候需要从最后一行向上遍历,不要忘我们第一行是用来标记0元素的横坐标的,如果从第一行开始遍历,就有可能将标记的0元素横坐标改变。为什么要从第二列开始遍历:同样第一列是用来存储0元素的纵坐标的,所以我们等一行的其他列都遍历完后再回过头来看第一列,当一行的其他列都遍历完,最后我们判断flag是否为true,也就是判断第一列原本有没有0元素存在,如果flag为true,那么我们就将第一列的元素置为0。)

e0e8de0e5837434b9b1c6b065e97ac0f.png

🧨三、代码实现(内有超详细的注释)

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        // int flag_row0 = false; // 用于标记第一行是否有0,可以用第一列的第一个元素代替,从而减少了一个变量


        int flag_col0 = false; // 用于标记第一列是否有0
        int row = matrix.size(); // 一共有多少行
        int col = matrix[0].size(); // 一共有多少列

        // 遍历一遍矩阵
        // 用第一行和第一列标记矩阵中0元素的坐标
        // 并且判断第一列是否本身就存在0
        for (int i = 0; i < row; ++i)
        {
            // 只要第一列存在一个0,就将第一列标记
            if (!matrix[i][0])
            {
                flag_col0 = true;
            }

            // 标记每个0元素的下标
            // 由于第一列已经单独判断了,所以这里只用从第二列开始遍历
            for (int j = 1; j < col; ++j)
            {
                // 如果当前元素为0,就将它的横纵坐标分别在第一行和第一列中进行标记
                if (matrix[i][j] == 0)
                {
                    // 注意:这里的i为纵坐标,j为横坐标
                    matrix[0][j] = 0; // 第一行用于标记横坐标
                    matrix[i][0] = 0; // 第一列用于标记纵坐标
                }
            }
        }

        // 再遍历一遍矩阵,只要当前元素的横坐标、纵坐标有一个被标记过,就将其置为0
        // 注意:这时遍历时就不能从第一列开始遍历了,如果第一列本身存在0,我们遍历完第一列就将第一列全部置成0了,导致我们刚才标记的坐标改变
        // 所以我们需要从后向前遍历。
        // 每行的第一个元素也要最后判断,如果先判断的话就跟先遍历第一行是一样的了
        // 如果第一列本身存在0,那么从每行第一个元素开始遍历,就会将第一个元素置成0,导致我们标记的坐标改变。

        for (int i = row - 1; i >= 0; --i)
        {
            // 由于下面会单独判断第一列元素,所以这里还是从第二列开始遍历
            for (int j = 1; j < col; ++j)
            {
                // 如果当前元素的横坐标、纵坐标有一个被标记过,就将其置为0
                if (!matrix[0][j] || !matrix[i][0])
                {
                   matrix[i][j] = 0;
                }
            }

            // 判断第一列是否本身就有0,如果有就将第一列的元素置成0,如果没有就不管了
            if (flag_col0)
            {
                 matrix[i][0] = 0;
            }
        }
    }
};

🦀总结

今天分享的题就到这了,相信大家都能够看懂,如果大家有什么解决不了的问题,欢迎大家评论区留言或者私信告诉我。如果感觉对自己有用的话,可以点个赞或关注鼓励一下博主,我会越做越好的,感谢各位的支持。🦀最后再次祝大家国庆节快乐。

901b647aff254d83abe29eeb8c67cfc2.gif

 

评论 69
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不一样的烟火a

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

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

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

打赏作者

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

抵扣说明:

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

余额充值