二分查找防止溢出

二分查找作为程序员的一项基本技能,是面试官最常使用来考察程序员基本素质的算法之一,也是解决很多查找类题目的常用方法,它可以达到 O(logN) 的时间复杂度。

标准二分查找模板
前提条件
必须有序。一般是从小到大有序。

要点
总体上来说:三个变量(左边界、右边界、中间值) + 判断条件(右边界更新条件和左边界更新条件)+ 返回值。

循环条件: left <= right
中间位置计算: mid = left + ((right -left) >> 1)
左边界更新:left = mid + 1
右边界更新: right = mid - 1
返回值: mid(找到) 或者 -1(没找到)
坑点
计算中间值导致的数据越界。一般我们都是定义左边界(left)和右边界(right)都使用 int 类型,如果 left 和 right 足够大,mid = (left + right)/2,可能会由于 left+right 导致 int 数据类型越界。所以安全的写法是 mid = left + ((right - left) >> 1) 或者 mid = left + (right - left) / 2,推荐使用右移操作,因为右移比除法快。

核心代码
需要完成一个合法性检查函数,该函数的目的是使用 mid 这个分界点,根据题目进行合法性判断。如果合法,则右边界更新,看是否有更小的 mid 符合要求;如果不合法,则左边界更新,看是否有更大的 mid 符合要求。

该合法性检查函数原型如下:

bool check(int mid) {
//根据题目要求,写出合法性检查函数
}
难点
最终的查找位置,也就是需要求的 answer 的位置。我们一共有 left、right、mid 这三个位置。根据题意确认哪个变量是我们需要的值。

模板代码
C++模板

class BinarySearch {
    private int check(int[] nums, int x, int target) {
        if (nums[x] == target) {
            return 0;
        } else if (nums[mid] > target) {
            return 1;
        } else {
            return -1;
        }
    }
 
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            if (0 == check(nums, mid, target)) return mid;
            else if (1 == check(nums, mid, target)) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }
}
C模板
/*
输入参数:
    nums   要搜索的有序数组
    len    数组长度
    target 搜索目标
*/
int binary_search(int nums[], int len, int target) {
    int left = 0;
    int right = len-1;
    int mid;
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (target == nums[mid]) {
            return mid;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return -1;
}
特别说明
1、我们的循环条件中包含了 left == right 的情况,则我们必须在每次循环中改变 left 和 right 的指向,以防止进入死循环。

2、循环终止的条件有两个。一是找到目标;二是没有找到目标,即 left > right 。

3、防止计算 mid 的时候出现数据溢出。参考上面特别说明的“坑点”。

4、left + ((right -left) >> 1)。对于目标区域长度为奇数而言,是处于正中间的;对于长度为偶数而言,是中间偏左的。因此左右边界相遇时,只会是以下两种情况:

left/mid , right (left, mid 指向同一个数,right指向它的下一个数)。
left/mid/right (left, mid, right 指向同一个数)。
即因为 mid 对于长度为偶数的区间总是偏左的,所以当区间长度小于等于 2 时,mid 总是和 left 在同一侧。
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值