73. Set Matrix Zeroes


Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0‘s.

You must do it in place.

Example 1:

Input: matrix = [[1,1,1],[1,0,1],[1,1,1]]
Output: [[1,0,1],[0,0,0],[1,0,1]]

Example 2:

Input: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
Output: [[0,0,0,0],[0,4,5,0],[0,3,1,0]] 

Constraints:

  • m == matrix.length
  • n == matrix[0].length
  • 1 <= m, n <= 200
  • -2^31 <= matrix[i][j] <= 2^31 - 1

Follow up:

  • A straightforward solution using O(mn) space is probably a bad idea.
  • A simple improvement uses O(m + n) space, but still not the best solution.
  • Could you devise a constant space solution?

据说这题是 CareerCup 上的原题,也不算难,虽然博主也是看了网上的解法照着写的,但是下次遇到绝对想的起来。这道题中说的空间复杂度为 O(mn) 的解法自不用多说,直接新建一个和 matrix 等大小的矩阵,然后一行一行的扫,只要有0,就将新建的矩阵的对应行全赋0,行扫完再扫列,然后把更新完的矩阵赋给 matrix 即可,这个算法的空间复杂度太高。将其优化到 O(m+n) 的方法是,用一个长度为m的一维数组记录各行中是否有0,用一个长度为n的一维数组记录各列中是否有0,最后直接更新 matrix 数组即可。这道题的要求是用 O(1) 的空间,那么就不能新建数组,就用原数组的第一行第一列来记录各行各列是否有0.

- 先扫描第一行第一列,如果有0,则将各自的 flag 设置为 true
- 然后扫描除去第一行第一列的整个数组,如果有0,则将对应的第一行和第一列的数字赋0
- 再次遍历除去第一行第一列的整个数组,如果对应的第一行和第一列的数字有一个为0,则将当前值赋0
- 最后根据第一行第一列的 flag 来更新第一行第一列

解法一:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        bool rowZero = false, colZero = false;
        for (int i = 0; i < m; ++i) {
            if (matrix[i][0] == 0) colZero = true;
        }
        for (int i = 0; i < n; ++i) {
            if (matrix[0][i] == 0) rowZero = true;
        } 
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                if (matrix[i][j] == 0) {
                    matrix[0][j] = 0;
                    matrix[i][0] = 0;
                }
            }
        }
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                if (matrix[0][j] == 0 || matrix[i][0] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        if (rowZero) {
            for (int i = 0; i < n; ++i) matrix[0][i] = 0;
        }
        if (colZero) {
            for (int i = 0; i < m; ++i) matrix[i][0] = 0;
        }
    }
};

我们可以稍微优化一下上面的解法,少用几个 for 循环,这里只用一个变量 colZero 来表示首列是否有0存在即可,首行不用单独去记录,因为其是否为0会在 matrix[0][0] 中记录。这里只需要两个嵌套 for 循环就可以了,第一个嵌套 for 循环的i是从0开始遍历的,若首列有0,则将 colZero 标记为 true。内层 for 循环的j是从1开始遍历的,若 matrix[i][j] 为0了,则将 matrix[0][j] 和 matrix[i][0] 都赋值为0。然后开始第二个嵌套 for 循环,外层的 for 循环的i是从 m-1 往前遍历到0,内层的 for 循环的j是从 n-1 往前遍历到1,若 matrix[0][j] 和 matrix[i][0] 中任意一个为0了,则将 matrix[i][j] 赋值为0,同时在外层 for 循环的遍历过程中,判断若 colZero 为 true,则将 matrix[i][0] 赋值为0,这就保证了首列被更新了,参见代码如下:

解法二:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        bool colZero = false;
        for (int i = 0; i < m; ++i) {
            if (matrix[i][0] == 0) colZero = true;
            for (int j = 1; j < n; ++j) {
                if (matrix[i][j] == 0) {
                    matrix[0][j] = 0;
                    matrix[i][0] = 0;
                }
            }
        }
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 1; --j) {
                if (matrix[0][j] == 0 || matrix[i][0] == 0) {
                    matrix[i][j] = 0;
                }
            }
            if (colZero) matrix[i][0] = 0;
        }
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/73

类似题目:

Game of Life

Number of Laser Beams in a Bank

Minimum Operations to Remove Adjacent Ones in Matrix

Remove All Ones With Row and Column Flips II

参考资料:

https://leetcode.com/problems/set-matrix-zeroes/

https://leetcode.com/problems/set-matrix-zeroes/solutions/26014/any-shorter-o-1-space-solution/

LeetCode All in One 题目讲解汇总(持续更新中…)

(欢迎加入博主的知识星球,博主将及时答疑解惑,并分享刷题经验与总结,快快加入吧~)

知识星球 喜欢请点赞,疼爱请打赏❤️.

微信打赏

|

Venmo 打赏


—|—


转载请注明来源于 Grandyang 的博客 (grandyang.com),欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 grandyang@qq.com

💰


微信打赏


Venmo 打赏

(欢迎加入博主的知识星球,博主将及时答疑解惑,并分享刷题经验与总结,试运营期间前五十位可享受半价优惠~)

×

Help us with donation