面试题 01.08. 零矩阵-Java-一题三解

1.题目

2.思路

方法一:队列存储。时间O(N * M * Max(N, M), 近似 O(N * N * N))。空间O(N * N)

第一反应是,先挨个遍历,找到0,然后把行和列都置换为0,然后仔细一想,这样不对。因为这样会导致,后面本来不是0,不会产生让行列都换成0,但是由于前面的0,让后面非0的数字变成了0,这样就会一直错误遍历下去。

改进思路:记录一下哪些初始位置为0.只改变这些位置的行和列肯定不会出错。比如可以采取用一个vis[][]记录。我这里采取的是用队列存储,代码如下:

class Solution {
    public void setZeroes(int[][] matrix) {
        int n = matrix.length;
        if(n == 0) return ;
        int m = matrix[0].length;
        Deque<int[]>d = new ArrayDeque();
        for(int i = 0 ; i < n ; i++){
            for(int j = 0 ; j < m; j++){
                if(matrix[i][j] == 0)
                    d.add(new int[]{i, j});
            }
        }
        while(!d.isEmpty()){
            int[]cur = d.poll();
            int x = cur[0], y = cur[1];
            putZero(matrix, x, y);
        }
        return ;
    }
    public void putZero(int[][] matrix, int x, int y){
        int n = matrix.length;
        int m = matrix[0].length;
        for(int i = 0 ; i < n ; i++)
            matrix[i][y] = 0;
        for(int j = 0 ; j < m; j++)
            matrix[x][j] = 0;
        return ;
    }
}

方法二 :借用两个临时变量,时间复杂度O(N * M)空间复杂度O(1)

上面的方法通过时间为1ms,超过50%。说明还有更快的方法。

核心思想有两点

  • 1.让第一行和第一列的元素记录所在行列是否有0。(这样会导致原来第一行和第一列是否有0不确定,所以需要第2点,用两个临时变量记录)
  • 2.用一个rowFlag记录第一列不同行的初始元素是否有0,只要有一个0,那么就赋值为true;用一个colFlag记录第一行不同列的初始元素是否有0,只要有一个0,那么就赋值为true;

步骤:

  1. 遍历第一行或者第一列,赋值rowFlag 和 colFlag
  2. 遍历其他行和其他列,赋值给第一行和第一列
  3. 先更新除第一行第一列的所有元素
  4. 最后更新第一行和第一列额元素
class Solution {
    public void setZeroes(int[][] matrix) {
        int n = matrix.length;
        if(n == 0) return ;
        int m = matrix[0].length;
        // rowFlag记录第一列不同行是否有0
        // colFlag记录第一行不同列是否有0
        boolean rowFlag = false, colFlag = false;
        // 初始化
        for(int i = 0 ; i < n ; i++){
            if(matrix[i][0] == 0)
                rowFlag = true;
        }
        for(int j = 0 ; j < m ; j++){
            if(matrix[0][j] == 0)
                colFlag = true;
        }
        
        // 让第一行 和 第一列记录所在行列是否有0
        for(int i = 1 ; i < n ; i++){
            for(int j = 1 ; j < m ; j++){
                if(matrix[i][j] == 0){
                    matrix[0][j] = 0;
                    matrix[i][0] = 0;
                }
            }
        }

        // 除开第一行和第一列,替换为0
        for(int i = 1 ; i < n ; i++){
            for(int j = 1 ; j < m ; j++){
                if(matrix[i][0] == 0 || matrix[0][j] == 0)
                    matrix[i][j] = 0;
            }   
        }

        // 第一行和第一列替换
        if(rowFlag == true){
            for(int i = 0 ; i < n ; i++){
                matrix[i][0] = 0;
            }
        }

        if(colFlag == true){
            for(int j = 0 ; j < m; j++){
                matrix[0][j] = 0;
            }
        }

        return ;
    }
   
}

这时候时间为0ms, 超过100%。但是空间还可以继续优化。

方法三:借助一个临时变量,时间O( N * M), 空间O(1) 

在方法二的基础上,继续优化的话,回到方法二的两个核心思想

  • 1.让第一行和第一列的元素记录所在行列是否有0。(这样会导致原来第一行和第一列是否有0不确定,所以需要第2点,用两个临时变量记录)
  • 2.用一个rowFlag记录第一列不同行的初始元素是否有0,只要有一个0,那么就赋值为true;用一个colFlag记录第一行不同列的初始元素是否有0,只要有一个0,那么就赋值为true;

这里的改进其实就是第 1 点不变,然后第2 点用一个变量来记录第一行和第一列元素是否初始位置有0.

我这里选取的是一个整形变量rowAndColFlag,初始值赋值为0,第一行第一列初始位置是否为0,有以下四种情况

  • rowAndColFlag == 0 代表第一行第一列都不为0

  • rowAndColFlag == 1 代表第一行不为0, 第一列为0

  • rowAndColFlag == -1 代表第一行为0, 第一列 不为0

  • rowAndColFlag == 2 代表第一行为0, 第一列为0;

其余步骤和方法二类似即可。

class Solution {
    public void setZeroes(int[][] matrix) {
        int n = matrix.length;
        if(n == 0) return ;
        int m = matrix[0].length;
       
        // rowAndColFlag == 0 代表第一行第一列都不为0
        // rowAndColFlag == 1 代表第一行不为0, 第一列为0
        // rowAndColFlag == -1 代表第一行为0, 第一列 不为0
        // rowAndColFlag == 2 代表第一行为0, 第一列为0;
        int rowAndColFlag = 0;
        // 初始化第一列
        for(int i = 0 ; i < n ; i++){
            if(matrix[i][0] == 0)
                rowAndColFlag = 1;
        }

        for(int j = 0 ; j < m ; j++){
           if(rowAndColFlag == 1){
               if(matrix[0][j] == 0){
                   rowAndColFlag = 2;
                   break;
               }
                    
           }
           else{
                if(matrix[0][j] == 0){
                    rowAndColFlag = -1;
                    break;
                }
                    
           }
        }
        // System.out.println(rowAndColFlag);
        // 让第一行 和 第一列记录所在行列是否有0
        for(int i = 1 ; i < n ; i++){
            for(int j = 1 ; j < m ; j++){
                if(matrix[i][j] == 0){
                    matrix[0][j] = 0;
                    matrix[i][0] = 0;
                }
            }
        }

        // 除开第一行和第一列,替换为0
        for(int i = 1 ; i < n ; i++){
            for(int j = 1 ; j < m ; j++){
                if(matrix[i][0] == 0 || matrix[0][j] == 0)
                    matrix[i][j] = 0;
            }   
        }

        // 第一行和第一列替换
        if(rowAndColFlag == 1 || rowAndColFlag == 2){
            for(int i = 0 ; i < n ; i++){
                matrix[i][0] = 0;
            }
        }

        if(rowAndColFlag == -1 || rowAndColFlag == 2){
            for(int j = 0 ; j < m; j++){
                matrix[0][j] = 0;
            }
        }

        return ;
    }
   
}

3.结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值