zoukankan      html  css  js  c++  java
  • 力扣73——矩阵置零

    准备开一个力扣解题的系列,督促自己每天刷题,就从今天开始。

    原题

    给定一个 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) 的额外空间,但这仍然不是最好的解决方案。
    • 你能想出一个常数空间的解决方案吗?

    原题url:https://leetcode-cn.com/problems/set-matrix-zeroes/

    解法

    其实题目本身不难,只要判断出哪些数字是0,将其所在行和列记录一下, 最终全部置0即可,关键在于你所需要消耗的空间是多少。

    用一个数字

    首先我想到的是用一个数字进行表示,用二进制表示,一共m + n位,其中前m位表示行,后n位表示列,矩阵中哪个数字为0,则其行列所在位的数字为1,也就是加上相应的二进制数。为了不重复添加,可以用&进行判断。来看看代码是什么:

    class Solution {
        public void setZeroes(int[][] matrix) {
            // 转化为二进制后,前m位表示列,后n位表示行
            int temp = 0;
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[i].length; j++) {
                    if (matrix[i][j] != 0) {
                        continue;
                    }
    
                    // 第j列是否已经被设置为0
                    int num = 1 << (matrix.length + j);
                    if ((temp & num) != num) {
                        // 如果没有,则加上
                        temp += num;
                    }
    
                    // 第i行是否已经被设置为0
                    num = 1 << i;
                    if ((temp & num) != num) {
                        // 如果没有,则加上
                        temp += num;
                    }
                }
            }
    
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[i].length; j++) {
                    // 第j列是否已经被设置为0
                    int numCol = 1 << (matrix.length + j);
                    // 第i行是否已经被设置为0
                    int numRow = 1 << i;
                    if ((temp & numRow) == numRow || (temp & numCol) == numCol) {
                        // 如果有,则设置当前值为0
                        matrix[i][j] = 0;
                    }
                }
            }
        }
    }
    

    理论上没什么问题,提交之后报错。当mn很大时,数字会很大,这个时候temp会越界。我想着是不是求2的幂用Math.pow(),并且 temp 的类型改为 long ,是不是就可以了,说干就干:

    class Solution {
        public void setZeroes(int[][] matrix) {
            // 转化为二进制后,前m位表示列,后n位表示行
            long temp = 0;
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[i].length; j++) {
                    if (matrix[i][j] != 0) {
                        continue;
                    }
    
                    // 第j列是否已经被设置为0
                    long num = (long)Math.pow(2, matrix.length + j);
                    if ((temp & num) != num) {
                        // 如果没有,则加上
                        temp += num;
                    }
    
                    // 第i行是否已经被设置为0
                    num = (long)Math.pow(2, i);
                    if ((temp & num) != num) {
                        // 如果没有,则加上
                        temp += num;
                    }
                }
            }
    
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[i].length; j++) {
                    // 第j列是否已经被设置为0
                    long numCol = (long)Math.pow(2, matrix.length + j);
                    // 第i行是否已经被设置为0
                    long numRow = (long)Math.pow(2, i);
                    if ((temp & numRow) == numRow || (temp & numCol) == numCol) {
                        // 如果有,则设置当前值为0
                        matrix[i][j] = 0;
                    }
                }
            }
        }
    }
    

    好吧,依然不可以,看来确实很大,最终还是溢出变成负数了。看来得另寻他法了。

    利用矩阵本身

    如果1个数字不够,那么多来几个数字应该也是不够用的,而且如果用的太多也可能会增长到m + n,空间依旧比较多。这个时候我也想不出来,看了看别人的解法,让我顿时领悟——利用矩阵本身。

    就是利用矩阵的第一行和第一列来记录需要置零的行和列,至于第一行和第一列是否需要置零,则可以单独拿两个 boolean 对象来表示。(怎么好的思路,为啥我就是没想到呢)来看看代码:

    class Solution {
        public void setZeroes(int[][] matrix) {
            // 用第一行和第一列表示当前行和当前列是否需要置0
            // 单独计算第一行和第一列是否需要置0
    
            int row = matrix.length;
            int col = matrix[0].length;
            // 第一行是否需要置0
            boolean row0 = false;
            for (int i = 0; i < col; i++) {
                if (matrix[0][i] == 0) {
                    row0 = true;
                    break;
                }
            }
            // 第一列是否需要置0
            boolean col0 = false;
            for (int i = 0; i < row; i++) {
                if (matrix[i][0] == 0) {
                    col0 = true;
                    break;
                }
            }
    
            // 判断每一行每一列是否需要置0
            for (int i = 1; i < row; i++) {
                for (int j = 1; j < col; j++) {
                    if (matrix[i][j] != 0) {
                        continue;
                    }
                    
                    matrix[i][0] = matrix[0][j] = 0;
                }
            }
            // 置0
            for (int i = 1; i < row; i++) {
                for (int j = 1; j < col; j++) {
                    if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                        matrix[i][j] = 0;
                    }
                }
            }
    
            // 第一行是否需要都置0
            if (row0) {
                for (int i = 0; i < col; i++) {
                    matrix[0][i] = 0;
                }
            }
            // 第一列是否需要都置0
            if (col0) {
                for (int i = 0; i < row; i++) {
                    matrix[i][0] = 0;
                }
            }
        }
    }
    

    终于通过了,执行用时:2ms,内存消耗:43.5MB。那么是否可以继续优化呢?

    利用矩阵本身 优化

    首先,需要第一行和第一列都判断一遍的吗?可以只判断其中一个即可,比如只判断第一列是否需要置零,那么第一行是否需要置零就可以依赖matrix[0][0]了。在置零的时候,也是将第一列单独判断即可。

    需要注意的是,置零操作需要从后往前,因为matrix[0][0]会有双重含义,所以最后判断即可。来看看代码:

    class Solution {
        public void setZeroes(int[][] matrix) {
            // 第一列是否需要置零
            boolean col0 = false;
            int row = matrix.length;
            int col = matrix[0].length;
    
            // 判断是否需要置零
            for (int i = 0; i < row; i++) {
                // 如果第一列不需要置零,并且第一列有数字是0,则col0设置为true
                if (!col0 && matrix[i][0] == 0) {
                    col0 = true;
                }
    
                for (int j = 1; j < col; j++) {
                    if (matrix[i][j] == 0) {
                        matrix[i][0] = matrix[0][j] = 0;
                    }
                }
            }
    
            // 置零,从后往前开始,因为如果从前往后,第一行如果因为第一列置为0,会对之后结果误导
            for (int i = row - 1; i >= 0; i--) {
                // 第一列不动
                for (int j = col - 1; j >= 1; j--) {
                    if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                        matrix[i][j] = 0;
                    }
                }
                // 第一列置零
                if (col0) {
                    matrix[i][0] = 0;
                }
            }
        }
    }
    

    总结

    以上就是这道题目我的解答过程了,不知道大家是否理解了。我准备把我刷力扣的过程记录下来,作为这个系列的内容,希望能和大家多多分享。

    有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。

    https://death00.github.io/

    公众号:健程之道

  • 相关阅读:
    每天一道LeetCode--141.Linked List Cycle(链表环问题)
    每天一道LeetCode--119.Pascal's Triangle II(杨辉三角)
    每天一道LeetCode--118. Pascal's Triangle(杨辉三角)
    CF1277D Let's Play the Words?
    CF1281B Azamon Web Services
    CF1197D Yet Another Subarray Problem
    CF1237D Balanced Playlist
    CF1239A Ivan the Fool and the Probability Theory
    CF1223D Sequence Sorting
    CF1228D Complete Tripartite
  • 原文地址:https://www.cnblogs.com/death00/p/12081269.html
Copyright © 2011-2022 走看看