【LeetCode 30天挑战活动】Day 21. Leftmost Column with at Least a One

题目描述

有一个自定义的01矩阵(即里面的元素非0即1),矩阵的每一行都是非倒序
目的是找到第一个出现’1’的列,返回这个列值,如果没有(即元素全为0),就返回-1
限制:
You can’t access the Binary Matrix directly. You may only access the matrix using a BinaryMatrix interface:
BinaryMatrix.get(x, y) returns the element of the matrix at index (x, y) (0-indexed).
BinaryMatrix.dimensions() returns a list of 2 elements [m, n], which means the matrix is m * n.

解题思路

法一: 暴力法。害,碰到这种题第一反应总是暴力法,疯狂遍历,也不用多解释了。但这题暴力法无法通过,会TLE(不过它是提示你 You made too many calls to BinaryMatrix.get().

法二: 二分搜索(官方hint1)(时间复杂度O(MlogN))
要理解这个做法,首先要对题目中的非倒序有一个正确的理解,所谓非倒序,其实是在说在每一行中,0和0放在一起,1和1放在一起,而0的那堆会放在1的那堆的左边。
补充一个对于non-decreasing的理解:

The difference being that in an increasing sequence, for x(n) and x(n+1), x(n+1) > x(n) whereas in a non-decreasing sequence, x(n+1) >= x(n)

那么要找到满足题意的那个列,其实是在找每一行中第一个1是在哪一列出现的,最后找到列数最小的那个。那么在每一行应用二分算法就很好理解了。

class Solution {
    public int leftMostColumnWithOne(BinaryMatrix binaryMatrix) {
        List<Integer> dimension = binaryMatrix.dimensions();
        int row = dimension.get(0);
        int column = dimension.get(1);
        
        if (row <= 0 || column <= 0)
            return -1;
        
        int res = column;
        
        for (int i = 0; i < row; ++i) {
            // binary search to find the first one
            int lo = 0, hi = column - 1;
            
            // one doesn't exist
            if (binaryMatrix.get(i, hi) == 0)
                continue;
            
            while (lo < hi) {
                int mid = lo + (hi - lo) / 2;
                
                if (binaryMatrix.get(i, mid) == 1) {
                    // refer to the left part
                    hi = mid;
                }
                else {
                    lo = mid + 1;
                }
            }
            
            res = Math.min(res, lo);
        }
        
        return (res == column)? -1 : res;
    }
}

法三: 优化解(官方hint2)(时间复杂度O(M + N))
一个挺tricy的方法,见见世面,具体步骤如下:
从自定义矩阵的右上角开始(这个开始位置同样也是因为non-decreasing的定义),
(1)如果当前位置是1,就向左移动
(2)如果当前位置是0,就向下移动
最后会产生两种出界:
(1)左出界,意味着我出界前遇到的那个1所在列就是我要找的结果
(2)下出界,意味着我一路过来遇到的最后一个1所在列就是我要找的结果

稍微分析一下这个方法的内涵,考虑任何一个位置(i, j)

  • 如果它为0,那么它的左边一定不会出现1,可以直接放弃这一行去看下一行
  • 如果它为1,那么要去它的左边看看是否还有更左的1
  • 最左的1一定不会出现在当前行的上面,因为上一点已经检查过了(↑)
  • 左出界,意味着在这一行一路过来都是1,那么出界前遇到的那个就是最左的1(其实也就是第0列
  • 下出界,意味着是在遇到最左的1之后又遇到了它左边的0,然后一直往下走没能再遇到更左的1,所以只能一直向下直至出界

根据这个分析可以写出下面的代码:

    public int leftMostColumnWithOne(BinaryMatrix binaryMatrix) {
        List<Integer> dimension = binaryMatrix.dimensions();
        int row = dimension.get(0);
        int column = dimension.get(1);
        
        if (row <= 0 || column <= 0)
            return -1;
        
        int r = 0, c = column - 1;
        int res = -1;
        
        while (r < row && c >= 0) {
            if (binaryMatrix.get(r, c) == 0) {
                r++;
            }
            else {
                res = c;
                c--;
            }
        }
        
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值