一文看懂二分查找

二分查找原理

二分查找的原理不难理解,即在一个有序数组中,每次将数组中间值与目标值进行比较,从而判断目标值在中间值的左边还是右边。
由于每次可排除一半的数,因此二分查找的时间复杂度为log2n。对于100万个有序数据,利用二分查找仅需搜索20次,效率非常高。mysql的B+树叶子节点的数据查找,用的就是二分查找。

代码编写

中间值

第一次写二分查找的代码时,都会有一个疑惑,那就是如果数组的个数是奇数和偶数时,中间值要不要分别处理?相信当你仔细去区分时,十有八九会被绕晕。我们完全可以不用考虑这个情况。

边界处理

二分查找的难点在于边界处理,解决了这个问题,二分查找的代码编写也就变得很简单了。下面介绍常用的两种边界规定方式,以递增数组为例。

  • 左闭右开
public int searchInsert(int[] nums, int target) {
        // 指针初始化,这里体现左闭右开[0, n)
        int l = 0;  // 左指针
        int r = nums.length;  // 右指针 
        while (l < r){
            int mid = l + (r - l) / 2; // 若采用(r + l) / 2 会有超出int最大值的风险
            if (nums[mid] > target){
                r = mid; // 右指针等于mid。(体现右开原则)
            }else if (nums[mid] < target){
                l = mid + 1; // 左指针等于mid + 1。 (体现左闭原则)
            }else {
                return mid;
            }
        }
        return -1;
    }

开始时,我们便把nums.length作为右指针,而数组的最大下标为nums.lenth - 1,所以这里第一次体现了左闭右开(右指针的值我们无需考虑,因为已经越界了)。
当我们把中间值与目标值进行对比时,若中间值大于目标值,说明目标值存在于左半部分,令r = mid。(由于是右闭,此时mid的值我们无需再考虑了,因此直接r = mid)。若中间值小于目标值时,说明目标值存在于右半部分,令l = mid + 1。这里为什么要+ 1呢?因为左边是闭区间,此时mid的值我们无需考虑了,因此l = mid + 1,跳过mid值。若中间值等于目标值,那恭喜你找到了目标值,直接返回即可。

  • 左闭右闭
public int searchInsert(int[] nums, int target) {
        // 指针初始化,这里体现左闭右闭[0, n - 1]
        int l = 0;  // 左指针
        int r = nums.length - 1;  // 右指针
        while (l <= r){
            int mid = l + (r - l) / 2; // 若采用(r + l) / 2 会有超出int最大值的风险
            if (nums[mid] > target){
                r = mid - 1; // 右指针等于mid。(体现右闭原则)
            }else if (nums[mid] < target){
                l = mid + 1; // 左指针等于mid + 1。 (体现左闭原则)
            }else {
                return mid;
            }
        }
        return -1;
    }

与左闭右开相比,初始时我们的右指针变为了nums.length - 1,这也是体现了左闭右闭原则。
当中间值mid大于目标值时,这里我们就得把 r = mid - 1,以排除mid值。
与左闭右开的while(l < r)相比,这里判断条件为啥是while(l <= r)呢?因为当l == r时,[l, r)已经是个空区间了,而[l, r]区间还有一个值l,需要继续做下一步的判断。
其它情况同左闭右开。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值