LintCode | Easy | 二分查找 | Binary Search

来源:互联网 发布:三端口网络的s参数 编辑:程序博客网 时间:2024/05/19 12:18

题目

给定一个排序的整数数组(升序)和一个要查找的整数target,
用O(logn)的时间查找到target第一次出现的下标(从0开始),
如果target不存在于数组中,返回-1。

样例

在数组 [1, 2, 3, 3, 4, 5, 10] 中二分查找3,返回2。

挑战

如果数组中的整数个数超过了2^32,你的算法是否会出错?

解题思路

首先看题目,实现二分查找,有一个需求是查找到target第一次出现的下标。
如果按常规方法

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

那不一定能查找到target第一次出现的下标。
正确处理二分查找及其变体,我们需要想清楚两个问题:
* 循环结束条件
* 循环过程中的区间划分和区间选择

重点是循环的每一步都完全排除不可能区间,终止条件一定要让循环能够终止

分析本题,left 和 right 为数组左右边界的下标,mid为数组中间元素下标,array为数组,target为要查找的数字。

初始条件:
left = 0, right = array.length - 1.
保持:
mid = (left + right)/2.
target > array[mid] ,说明[left, mid]区间内没有target,新区间为[mid + 1, right]。
target < array[mid],说明[mid, right]区间内没有target,新区间为[left, mid - 1]。
target = array[mid],因为数组允许重复,[left, right]区间内都有可能有target,
但是我们需要的是数组中第一个出现的target,故可以排除[mid + 1, right],新区间为[left, mid]。
结束:
如果target不存在于array中,最后结果必然是left > right,所以我们可以将终止条件设置为left > right。
如果target存在于array中,最后的结果是left == right ,终止条件为left == right。
所以终止条件为left >= right,循环条件即为left < right。

按此条件,在循环结束时必然是 left == right ,而此时并不能确定target在不在array中。
需要加一步判断,即在循环结束时,判断array[left] == target。
若等于则找到,返回left,若不等于则说明未找到,返回-1。

最后为了防止left + right的溢出,令mid = (right - left)/ 2 + left。

代码实现

   /**     *      * @param nums: The integer array.     * @param target: Target to find.     * @return: The first position of target. Position starts from 0.     */    public int binarySearch(int[] nums, int target) {        int left = 0;        int right = nums.length - 1;        int mid;        while(left < right)        {            mid = (right - left) / 2 + left;            if(target > nums[mid])                left = mid + 1;            else if(target < nums[mid])                right = mid - 1;            else                right = mid;        }        if(nums[right] == target)            return right;        return -1;    }
1 0