自己关于折半查找的一点理解

自己关于折半查找的一点理解

折半查找的代码虽然很简单,但自己老是用的不明不白,就给自己写个笔记,方便后面自己看。
第一种情况:在一个有序数组中查找某个元素。代码如下:

    public static int binarySearch(int[] num,int target){
        int left = 0;
        int right = num.length-1;
        while (left<=right){
            int mid = (left+right)/2;
            if(num[mid]<target){
                left = mid+1;
            }else if(num[mid]>target){
                right = mid-1;
            }else {
                return mid;
            }
        }
        //执行到这一步就是数组里面没有target这个元素
        return -1;
    }

这种是最简单的二分查找,只需要声明两个指针left,right来描述当前的查找范围,每一次查找都是使用left和right中间的数,根据比较情况对范围进行二分。而最关心的就是临界情况。如果待查找的范围内数据总数是奇数,例如:
现在总的数据个数为7,中间数为4,比较后就会在1-3和5-7两部分二选一再进行二分查找。
假设现在是1-3,中间数为2,比较后就会在1和3两部分二选一。
假设现在查找范围是1,并且此时有left=right,那么mid = (left+right)/2=left=right,如果满足条件,那么返回mid即可。如果这个1还不满足条件,那么就会出现left+1或者right-1,就会出现left>right,程序就停止了,返回-1。

现在假设总的数据个数为8,中间数就是4,比较后就会在1-3和5-8两部分二选一再进行二分查找。注意到1-3是奇数个数据,就不讨论了。5-8寻找中间数6,分为5和7-8两部分。5也不讨论了,看7-8如何二分,取中间数7,比较后出现三种情况:
要么相等,返回此时mid=7;
要么比7还要小,就又想往前搜,right=mid-1=7-1=6,出现left>right,程序停止,返回-1
要么比7还要大,就想往后搜,就搜索8,讨论同上面类似。

上述讨论是查找指定元素target,要么查找到返回,要么返回-1,但还有一种二分更牛逼,可以在有序数组中查找到大于等于target的数据中的最小值。代码如下:

    public static int binarySearch(int[] num,int target){
        int left = 0;
        int right = num.length-1;
        while (left<right){//这一步left<right
            int mid = (left+right)/2;
            if(num[mid]<target){
                left = mid+1;
            }else{//这一步不同
                right = mid;
            }
        }
        return left;//这一步不同
    }

虽然两个代码很类似,但是我觉得从思想上去理解有很大不同,并且很容易踩坑。
首先,这个代码在小于target时将范围的左边界变为mid+1,那么我们可以认为左边界之外即小于left的元素肯定是小于target的,并且右边界以及右边界之外的元素一定是大于等于target的,于是这个代码二分地缩小中间不确定的范围。
临界情况有两种:就是最后的范围大小为2和3时

当范围为2时,假设此时left=1,right=2,mid=1那么此时有两种情况:第一种比较完后left=mid+1=right=2,程序退出,此时right就是所求的值。这是因为此时mid=1还是小于target,而right=2就是大于等于target的,因此此时right就是所求的值。第二种就是比较完后right=mid=left=1,程序退出,此时right就是所求的值。

当范围为3时,假设此时left=1,right=3,mid=2,那么有两种情况:第一种比较完后left=mid+1,那么left走到了right,程序退出,此时right就是所求的值。这是因为此时mid=2还是小于target,而right=3就是大于等于target的,因此此时right就是所求的值。第二种就是比较完后right=mid=2,范围为2,讨论同上。
那么,从分析上面证明了这个程序是正确的。并且也可以看出,并不是简单从一半里面去查找了,而是缩小不确定的范围,直到两个边界重合,至此不确定的范围就为0了。
并且,不能简单修改上面代码来实现在有序数组中查找到小于等于target的数据中的最大值。例如下面的代码是错的:

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

这是因为走到范围为2的临界情况时会出现死循环,例如:
此时left=1,right=2,mid=1,但是此时num[mid]<=target一直成立,范围不更新,一直循环left=1,right=2,mid=1。
这是由mid = (left+right)/2;向下取整决定的。

其实说了那么多,我自己弄了一个笨方法,就用这个在有序数组中查找到大于等于target的数据中的最小值的程序来修改。例如,现在要在有序数组中查找target,那么将在有序数组中查找到大于等于target的数据中的最小值的结果进行验证,是否等于target,如果等于,返回这个结果,如果不等于,返回-1。
代码如下:

    public static int binarySearch(int[] num,int target){
        int left = 0;
        int right = num.length-1;
        while (left<right){
            int mid = (left+right)/2;
            if(num[mid]<=target){
                left = mid+1;
            }else{
                right = mid;
            }
        }
        //实现查找target功能
        if(num[left]==target) return left;
        return -1;
    }

并且如果要找到小于等于target的数据中的最大值。那么也是将在有序数组中查找到大于等于target的数据中的最小值的结果进行验证,如果等于target,那么返回这个结果,如果不等,那就返回这个结果-1。代码如下:

    public static int binarySearch(int[] num,int target){
        int left = 0;
        int right = num.length-1;
        while (left<right){
            int mid = (left+right)/2;
            if(num[mid]<=target){
                left = mid+1;
            }else{
                right = mid;
            }
        }
        //实现查找小于等于target的数据中的最大值
        if(num[left]==target) return left;
        return left-1;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值