算法通关村 | 二分查找

循环方式实现二分

public static int binarySearch(int[] array, int low, int high, int target) {
    while (low <= high) {
        int mid = (low + high)/2;
        if (array[mid] == target) {
            return mid; 
        } else if (array[mid] > target) {
            // 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
            high = mid -1;
        } else {
            // 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
            low = mid + 1;
        }
    }
    return -1;
}

在具体操作的时候可能有多种方式的,包括循环体中的 high = mid -1;low = mid + 1也有多种方式的,这需要与if后面的条件配合,我们不要给自己添麻烦,在理解的基础上熟记这种方式就行了。
如果代码写成这样子,只能得70分,因为有个很重要的细节没有处理。在计算机中,除的效率非常低,一般可以使用移位来代替,也就是

将:
int mid = (low + high) /2;
换成
int mid = (low + high)>>1;

如果这样的话,能得到80分,面试官可能会继续问,还会有什么问题。问题就是假如low和high很大的话,low + high可能会溢出。因此我们可以这么写:
int mid = low+(high - low)>>1;
只要 lowhigh没有溢出,上面的 mid 一定不会溢出。
你觉得可以得到90分,很可惜是0分,因为当你测试的时候,可能会出现死循环,例如原始序列是1到8,搜索3的时候就死循环了,为什么呢?
这是因为移位的运算符>>优先级比加减要低,所以上面的代码等价结构是这样的:
(low+(high - low))>>1
很明显这不是我们预期的。解决方法也很简单,加括号就行了。所以最终的代码就是:

public static int binarySearch(int[] array, int low, int high, int target) {
    while (low <= high) {
        int mid = low + ((high - low) >> 1);
        if (array[mid] == target) {
            return mid;
        } else if (array[mid] > target) {
            // 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
            high = mid - 1;
        } else {
            // 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
            low = mid + 1;
        }
    }
    return -1;
}

递归方式实现二分

public static int binarySearch1(int[] array, int low, int high, int target) {
    //递归终止条件
    if(low <= high){
        int mid = low + ((high - low) >> 1);
        if(array[mid] == target){
            return mid  ;  // 返回目标值的位置,从1开始
        }else if(array[mid] > target){
            // 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
            return binarySearch(array, low, mid-1, target);
        }else{
            // 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
            return binarySearch(array, mid+1, high, target);
        }
    }
    return -1;   //表示没有搜索到
}

元素中有重复的二分查找

假如在上面的基础上,元素存在重复,如果重复则找左侧第一个,请问该怎么做呢?
这里的关键是找到目标结果之后不是返回而是继续向左侧移动。第一种,也是最简单的方式,找到相等位置向左使用线性查找,直到找到相应的位置。

public static int search(int[] nums, int target) {
    if (nums == null || nums.length == 0)
        return -1;
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            //找到之后,往左边找
            while (mid != 0 && nums[mid] == target)
                mid--;
            if (mid == 0 && nums[mid] == target) {
                return mid;
            }    
            return mid + 1;
        }
    }
    return -1;
}

注意这里在找到之后的while循环结束后,为什么要返回mid + 1,而不是mid呢?这是因为此时nums[mid]已经不等于target了,例如假如序列为{1, 2, 2, 2, 2, 3, 3},当target = 3,当内层的while退出时,nums[mid]=2,因此我们必须返回mid + 1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值