二分查找的细节


二分查找的细节

1、 二分查找的使用场景

场景 一 (最经典的二分查找使用场景):给定一个升序数组int[] nums,查找是否存在目标值target,如果存在返回该值/返回true/返回该值的下标值,不存在返回 -1 / 返回 false

场景 二:现在我们要查找的目标值不止一个,返回该目标值的左边界/右边界/[左边界,右边界]

最开始我们学习的方法是什么呢?
答:我们可以使用最基础的二分查找找到目标值target,然后定义一个“指针index”向这个target的前后继续进行比较,如果nums[index] = target 就将index 加入到结果中(如果需要找到所有target的下标),但是这样难以保证二分查找对数级的复杂度

2、关注的细节问题

二分查找的思路很简单,但是有很多细节问题需要关注

  1. 定义左右指针 leftright 的取值,我们经常可以看到 right = nums.length - 1 / right = nums.length
  2. while 中的条件 是 left <= right 还是 left < right
  3. 进行再次二分时,leftright 到底是 加 1 还是 减1 涅?偶尔也是 right = mid,偶尔是 right = mid - 1
  4. 不清楚细节造成数组的索引越界

3、寻找一个 target(最基本的二分查找)

先给出二分查找的框架,其中 “…” 号的部分就是需要每次根据实际题目注意调整和容易出错的细节

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

使用一个技巧:不要在条件判断中出现 else ,将所有的情况用 else if 写清楚,就可以清楚地展现所有地细节

计算 mid 时需要防止溢出,代码中left + (right - left) / 2就和(left + right) / 2的结果相同,但是有效防止了 left 和 right 太大直接相加导致溢出

这是基本二分查找的代码:

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

下面我们分析 right 的取值 和 while 里面的终止条件是怎么回事?

  1. right = nums.length - 1 这是最后一个元素的索引,我们需要判断这个元素是否是题目要查找的 target ,所以我们的查找区间应该是 [left,right],即一个闭区间,那么我们的退出条件就应该是 left > right,即 left = nums.length 此时索引越界
  2. right = nums.length 这不是我们最后一个元素的索引不应该包含在查找的区间里面,所以我们的查找区间是 [ left, right),那么 while 中的退出条件就应该是 left = right,此时仍然是 left = nums.length索引越界的情况

小总结:我们必须要清楚进行搜索的区间是什么,然后去判断 while 循环应该终止的条件,同时应该考虑索引越界的情况,考虑是否每一个元素我们都进行了判断;这些会在后面的代码中体现

什么时候我们就不用再继续查找了呢?答案显而易见就是当我们找到目标值target的时候就不用继续查找了

if(nums[mid] == target) return mid;

如果没找到,就是 while 循环终止,返回 -1;

  1. while(left <= right) 的终止条件是 left == right + 1,写成区间的形式就是 [right + 1, right],或者带个具体的数字进去 [3, 2],可见这时候区间为空,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 -1 即可

  2. while(left < right) 的终止条件是 left == right,写成区间的形式就是 [left, right],或者带个具体的数字进去 [2, 2],这时候区间非空,还有一个数 2,但此时 while 循环终止了。也就是说这区间[2, 2]被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就是错误的

如果我们一定要用 while(left < right) ,那么我们就增加一个判断,保证最后一个数不漏掉

while(left < right){
   
    ...
}
return nums[left] == target ? left : -1
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值