二分法查找

力扣题目链接

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

要使用二分查找的前提是要保证 有序数组,且题目假设数组内元素不重复

二分法的思想就是不断的将剩余区间对半划分,在目标值所在的那个区间继续查找

关键要注意的是边界的处理,while(left<=right)还是while(left<right)

这决定于我们选择区间的方式一种是 左闭右闭 一种是左闭右开。

以下分析都基于区间的划分方式选择 左闭右闭 时 即[left,right]此时left=right是有意义的   所以需要 <=

同时当划分区间时就需要 right=mid-1 或者 left= mid+1,因为mid已经在上一个大区间内进行过判断了,重新划分的两个小区间不需要 nums[mid] 这个元素了

public int search(int[] nums, int target) {
        int left=0,right=nums.length-1,mid=(left+right)/2;
        while(left<=right){//target!=nums[mid]
            if(target<nums[mid]){
                right=mid-1;
                mid=(left+right)/2;
            }
            else if(target>nums[mid]){
                left=mid+1;
                mid=(left+right)/2;
            }
            else return mid;
        }
        return -1;
    }

跳出循环的情况分析:跳出循环的条件是 left > right

1、当target不在数组中 且大于数组中的所有元素时

此时left会不断向右,直到变成 nums.length+1,此时left=nums.length+1;right = nums.length-1

2、当target不在数组中 且小于数组中的所有元素时

此时right会不断向左,直到变成 -1,此时left=0;right = -1;

3、当target不在数组中 但位于数组的范围中时

left 会在第一个大于target 的位置

right 会在第一个小于target的位置

——————————————————————————————————————————

二分查找的两个相关题目

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

首先改变一下mid的计算方法 这种方法计算可以避免整数溢出 

可以理解为 起始位置加上区间长度的一半 ,这种方法可以在其他地方也替代(right+left)/2的写法

int mid = (right - left) / 2 + left;

然后对于插入位置,理解了上述所说的二分法跳出循环后 left 和 right 的所处位置,会发现只要插在 left 的位置就正好

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

我刚开始的做法,最不好的一点是我使用的二分法 在找到第一个目标值之后就退出二分法 之后在该位置的两边进行线性搜索 从而寻找目标元素的起始位置和结束位置 此时在最坏的情况下 就会造成时间复杂度为 O(n) 从而不符合题目要求

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

官方题解如下

其中find函数是先找到 第一个大于等于 目标值的 元素的位置和刚才分析的情况一样

此时再进行判断是 上述说的三种情况中的哪一种

第二个max 则是找一个大于目标值的数 所在的第一个位置

public int[] searchRange(int[] nums, int target) {
        int min=find(nums,target);
        if(min==nums.length||nums[min]!=target){
            return new int[]{-1,-1};
        }
        int max=find(nums,target+1)-1;
        return new int[]{min,max};
    }
    public int find(int[] nums, int target){
        int l=0;
        int r=nums.length;
        while(l<r){
            int mid=l+(r-l)/2;
            if(nums[mid]>=target){
                r=mid;
            }else{
                l=mid+1;
            }
        }
        return l;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值