二分查找概述

二分查找的前提:有序。

这些序信息能够确保你可以大胆的缩减空间,进行定位。

二分查找的两个关键点:

1.缩减规则的制定。

我们接触的二分查找很多是从有序数组中查找某个数,所以缩减规则比较单一。(大于目标值,往哪边走,小于往哪边走?)
但实际中还有很多复杂的场景,比如说寻找最近的k个数的问题。

【例子】在有序数组中二分查找某个数

(1)【缩减规则】 目标值与中间值的比较。(等于,大于,或者小于将导致不同的变化)

(2)这规则是容易看出来的。

public static int binarySearch(Integer[] srcArray, int des) {
    //定义初始最小、最大索引
    int low = 0;
    int high = srcArray.length - 1;
    //确保不会出现重复查找,越界
    while ((low <= high) && (low <= srcArray.length - 1)
            && (high <= srcArray.length - 1)) {
        //计算出中间索引值
        int middle = (high + low)/2 ;
        if (des == srcArray[middle]) {
            return middle;
        //判断下限
        } else if (des < srcArray[middle]) {
            high = middle - 1;
        //判断上限
        } else {
            low = middle + 1;
        }
    }
    //若没有,则返回-1
    return -1;
}

【例子】寻找最近的K个数

题目参考:【疑问】leetcode - 658. Find K Closest Elements【二分查找 + 双指针 + 找最近】

解法:(二分查找)

目标】本题的目标在于,在有序数组中寻找离 x 最近的k个数

规则观察】经过仔细观察,我们可以发现在k个相邻的元素中,最远的情况出现在该K个元素的最左端( kLleft )或者最右端( kRight )。接下来让我们来进行一下简单的推理:

我们此处认为,x一定是在区间的中间的部分,才能最近。

(1) 假如说 xkLeft>kRightx ,那么会导致 xi>kRightx ,其中 i<kLeft


jx>kRightxj>kRight

xkLeft>kRightx

jxxkLeft

kkRight+1xxkLeft

【规则制定】规则在于确定k个区间的起点。
(1) x - arr.get(mid) > arr.get(mid + k) - x 导致 右移1个单位 start = mid + 1
(2)否则,左移一个单位

class Solution {  
    public List<Integer> findClosestElements(List<Integer> arr, int k, int x) {  
        int start = 0, end = arr.size() - k;  
        while(start < end) {  
            int mid = start + (end - start) / 2;  
            if(x - arr.get(mid) > arr.get(mid + k) - x) {  
                start = mid + 1;  //起点在mid右边。(排除mid,因为mid到mid + k区间中有 k + 1 个元素)
            }else {  
                end = mid;  //起点在mid左边。(包括mid)
            }  
        }  
        return arr.subList(start, start + k);  
    }  
} 

2.边界控制

边界控制的细节经常让人感到头疼。下面分析一下,如何去理解这些边界。

我们此处设置几个变量来进行说明:
start:要查询区间的左端点。
end:要查询区间的右端点。
mid:区间靠近中间的点。(start +end)/ 2
target:待查找值。
(区间数值按从小到大排序)

接下来我们开始分析,
mid 大于 target

根据有序信息,以及有序的传递性,我们可以知道mid右边的区间的所有数值也大于target,所以我们相当于对mid及其右边的区间都已经进行了比较。(换句话说,右边区间数字和target的关系(大于关系)等同于mid与target的关系)
所以mid及其右边的区间已经没有了寻找的价值,将区间缩小,得到右端点 end = mid-1

当mid小于target时,也可以做类似的分析。( start = mid +1 )
……
区间缩减

区间就这样一步一步被缩减了。

【缩减后的效果】前面的缩减规则确定了,每一步一定会将不符合要求的区间排除出去,但是不确定剩下有没有目标。(剩下的有待进一步探索,类似于做题时候的排除法)
再次重申一下观点,此处认为缩减的过程,是在排除不符合要求的内容,因为那些真正比较过了(可确定为无用)。

那么最后区间会缩减成什么状态呢?

这里考虑3种情况,
start = end ,区间有一个元素。
start +1 =end,区间有两个元素。
start + 2 =end,区间有3个元素。(加入该种情况是为方便理解)

所有的缩减区间最后都会变为这3种情况的一种,(接下来考虑找不到目标值的情况)

情况3经过一步会变成情况1。(一次比较,能够消去两元素)。

情况2下一个状态有两种,其一是变为第一种情况,其二是终止。(这里说明了为什么不能将start == end设为终止条件,这种情况会将其跳过,直接从 start +1=end,变为start > end,因为同时排除了两个元素)

情况1经过一步比较,一定可以排除1个元素,导致start 大于end.

参考:
二分查找有几种写法?它们的区别是什么?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值