标准二分查找模板

二分查找作为程序员的一项基本技能,是面试官最常使用来考察程序员基本素质的算法之一,也是解决很多查找类题目的常用方法,它可以达到 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 的情况,则我们必须在每次循环中改变 leftright 的指向,以防止进入死循环。

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 在同一侧。

你可能感兴趣的:(OI,#,查找,二分查找,二分查找模板)