二分查找——基础版、改进版、平衡版

题目:

在这里插入图片描述

以下三种算法都没有问题!!!
针对一些细节进行优化或者不同的边界

基础版

/*
* 二分查找基础版
*   param:  待查找有序数组arr[]
*           target-待查找的目标值
*   return:
*           查找到了就返回索引,否则返回-1
* */

    public static int binarySearch(int[] arr, int target){
        int i = 0;            //左指针
        int j = arr.length-1; //右指针

        while (i <= j){
        	//int mid = (i+j)/2;
            int mid = (i+j) <<< 1;
            if(target < arr[mid]){      //目标在左边
                j = mid - 1;
            }
            else if (arr[mid] < target){    //目标在右边
                i = mid + 1;
            }
            else{
                return mid;     //找到了
            }
        }
        //  没查找到
        return -1;
    }

这里有几个问题:

    1. i<=j 还是i<j?哪个对?区别在哪?
    1. (i+j)/2还是(i+j) <<< 1?哪个对?为什么不行?
    1. 为什么都用小于符号?

答:
1、这里的i<=j意味着区间内还有未比较的元素。如果判定条件是i<j,存在当mid=i=j时,会跳出while循环。

2、
针对特殊情况,解释一下为什么不用(i+j)/2

在这里插入图片描述

在这里插入图片描述

使用**右移操作<<< 1**后(等效于除以2,向下取整)

在这里插入图片描述

3.都用小于符号,便于阅读代码,同时思路清晰,明了。

改进版

注意看改动处就行。主要是边界不一样,而且右指针所指向的数不参与比较。

//改进版,注意改动的地方
    public static int binarySearch(int[] arr, int target){
        int i = 0;
        int j = arr.length;     //第一处改
        // 此时的右指针所指向的数不参与比较,只作为边界,但i指针所指向的数参与比较

        while (i < j){          //第二处改
            int mid = (i + j) >>> 1;
            if (target < arr[mid]){
                j = mid;       //第三处改
            }
            else if (target > arr[mid]){
                i = mid + 1;
            }
            else {
                return mid;
            }
        }
        return -1;
    }

平衡版

针对基础版的特殊情况,当查找最左边的元素,此时假设while循环了 L 次,那么此时while里面比较了 L 次;但当查找最右边的元素,此时同样假设while循环了 L 次,那么此时while里面却比较了 2L 次。这就不平衡了。为了平衡点,见以下代码:

//  平衡版
    public static int binarySearch(int[] arr, int target){
        int i = 0;
        int j = arr.length;
        // 左闭右开区间,i指向的可能是目标,j指向的不是目标
        while (1 < j - i){      //  不在循环内找出,等范围只剩下1时,退出循环,在循环外比较arr[i]和target
                                //  循环内的平均比较次数减少了
            int mid = (i + j) >>> 1;
            if (target < arr[mid]){
                j = mid;
            }
            else {
                i = mid;    //  这里不用+1,有可能arr[mid]正是target,循环内只是比较了小于情况
            }
        }
        if(arr[i] == target){
            return i;
        }
        return -1;
    }
}

总结

  • 第一个算法代码,两边左右指针所指向的数可能参与比较。

  • 第二个算法代码,其右指针是作为边界指针,所指向的数不参与比较,但左指针所指向的数可能参与比较。

  • 第三个算法代码,主要是减少比较次数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值