Java-二分查找-leetcode 704、35、34

 二分查找

leetcode 704

        题目描述

704. 二分查找 - 力扣(LeetCode)

 

        理解及注意事项

        二分法原理上相对来说简单一些,就是通过中间值与目标值之间不断的比较进而缩小查找范围(left , right)。

        但是对于left 和 right  又有两种方案,即全闭[left , right] , 左闭右开 [left , right]。

        代码上体现主要在于while循环条件的变化以及缩小范围时right值的初始化更新时的变化。

         方案一:全闭范围

class Solution {
    public int search(int[] nums, int target) {
        //左闭右闭   [a,b]
        int left = 0 , right = nums.length - 1 ,mid , number;   //同下  right 同样需要考量,因为right代表的是索引
        while (left <= right){// 以区间范围为考量,[a,b] 对应 [1,2] , [2,2] 是否满足范围标准
                                //进而确定while循环条件是否是<=
            mid = left + ((right - left) >>1);
            number = nums[mid];
            if(number == target){
                return mid;
            }else if (number > target){
                right = mid - 1;   //因为本身包含右侧,所以需要进一步-1
            }else{
                left = mid + 1;    //因为本身包含左侧,所以需要进一步+1
            }
        }  
        return -1;
    }
}

         方案二:左闭右开

class Solution {
    public int search(int[] nums, int target) {
        //左闭右开 [a,b)
        int right = nums.length , left = 0  , mid , number ; //因为是左闭右开,[a,b) , 
                        // 所以核对区间范围 考虑是索引 所以是[0,length]
        while (left < right) { //同样核对区间 [a,b) [1,2) [1,1)是否满足 ,确定条件中是否是<=
            mid = left +  ((right - left) >>1);
            number = nums[mid];
            if (number == target){
                return mid ;
            }else if (number > target){
                right = mid;  // 因为本身就不包含右侧,所以不需要进行-1
            }else {
                 left = mid + 1;  //因为本身包含左侧,所以需要进一步+1
            } 
        }
        return -1;

    }
}

leetcode 35

        题目描述

35. 搜索插入位置 - 力扣(LeetCode)

         理解及注意事项

        这个题本质也是二分法,只是最后返回的是存在target的索引或插入位置。

        难点主要在插入位置的确定上,按照解题思路里的话来说,如果nums中不存在target,那么退出循环,left和right肯定是交叉的[right , left],那么插入位置应该在left上。

这里就不写左闭右开的情况了,只列出左闭右闭的情况

        解题代码

class Solution {
    public int searchInsert(int[] nums, int target) {
        //左闭右闭  [a,b]
        int left = 0 , right = nums.length -1 , mid = 0 ,number ;

        while( left <= right ){
            mid = left + ((right - left) >> 1);
            number = nums[mid];
            if (number == target){
                return mid;
            } else if(number > target){
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}

leetcode 34

        题目描述

        理解及注意事项

        U1S1,这个非递减数组,很离谱,其实就是升序数组的意思,真的很无语。

        我一开始的思路是二分法,在找到target之后,无限判断是否有与target相同的数,进而扩大start和end的范围。但是后来总是报OutOfRange的错误,所以就改了一下判断依据,非常巧妙地(加粗夸赞自己的傻瓜式思想!),因为&&如果前面的是false那就不判断后面的了,所以我在前面判断是否数组索引越界,然后后面放索引对应值与target的判定,这样就避免了OOR。

        稍微看了一下例子的思路,其实也相对来说简单,因为是升序顺序数组,上一个思路只用到了顺序数组的条件,并没有用到升序,佬们的思路也很简单,找到target的第一个索引比target大的下一个数的第一个索引-1,构成了[start,end]

        我的傻瓜式思想

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0 , right = nums.length -1 , mid , number ;
        int[] result = new int[2];
        while (left <= right){
            mid = left + ((right - left)>>1);
            number = nums[mid];
            
            if (number == target){
                int end = mid , start = mid;
                while(true){
                    if(start>=1 && nums[start-1] == target){
                        start--;
                    }else {
                        break;
                    }
                }
                while(true){
                    if(end<nums.length - 1 && nums[end+1] == target){
                        end++;
                    }else {
                        break;
                    }
                }
                result[0] = start;
                result[1] = end;
                return result;
            }else if( number > target){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        result[0] = -1;
        result[1] = -1;
        return result;
    }
}

佬们的牛逼思想

这个地方直接复制粘贴的,我跟着他的思路写了一个但是报错,到时候回来看一下。

这算是正常二分法的变种,就算nums[mid] == target ,也要继续收缩,进而获得border。需要注意的是 Right找LeftBorder , left找RightBorder!

class Solution {
    int[] searchRange(int[] nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一
        if (leftBorder == -2 || rightBorder == -2) return new int[]{-1, -1};
        // 情况三
        if (rightBorder - leftBorder > 1) return new int[]{leftBorder + 1, rightBorder - 1};
        // 情况二
        return new int[]{-1, -1};
    }

    int getRightBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else { // 寻找右边界,nums[middle] == target的时候更新left
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }

    int getLeftBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
                right = middle - 1;
                leftBorder = right;
            } else {
                left = middle + 1;
            }
        }
        return leftBorder;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值