二分查找的学习感悟


前言

博主是一名大二的学生,最近在学习Java,今天看到执梗大佬的一篇文章,感觉受益匪浅,因此写了一篇博客来总结学习。(推荐先看大佬的文章)

一、二分查找是什么?

二分查找又叫折半查找,它的算法思想非常简单,我们通过一个猜数字游戏来引入:
一天,聪明的小明正在和他的妹妹丽丽做游戏。小明对妹妹说,你在心里想一个0-100之间的数字,每次我猜一个数字,如果猜大了,你只要告诉我猜大了,如果猜小了你只需要告诉我猜小了,无论你想的是哪个数字,我总能在七次之内猜对。为何小明如此自信呢?让我们看看他猜数的过程

丽丽想的第一个数字是 71。小明:50 ->丽丽: 猜小了 -> 小明:75 ->丽丽:猜大了 -> 小明:73 -> 丽丽:猜大了 ->小明:72 -> 丽丽:猜大了 -> 小明:71 -> 丽丽:猜对了

我们看到,小明每次都是猜测候选集中间的数字,这样候选集大小每次减少一半,而 2 7 = 128 > 100 2^7=128>100 27=128>100,所以小明总能在七次之内猜出来!
这便是二分查找的基础思想,对于一个有序的数组 n u m s [ n ] nums[n] nums[n],查找的目标值为 t a r g e t target target,我们每次只要对中间值 n u m s [ m i d ] nums[mid] nums[mid] t a r g e t target target进行比较,判断 t a r g e t target target所在的区间即可。

1.二分查找:查找一个数

代码如下:

    public static int  test(int[] nums,int k){		//升序数组nums[]
        int left=0;									//left是左边界
        int right= nums.length-1;					//right是右边界
        while(left<=right){							//<=的目的是为了保证target
            int mid=left+(right-left)/2;            //防止溢出
            if(k==nums[mid])
                return mid;
            else if(k<nums[mid])
                right=mid-1;						//已经比较过mid
            else if(k>nums[mid])
                left = mid + 1;						//同上
        }
        return -1;
    }
为什么一定要强调left<=right,我们去掉“=”举个反例
nums[3]={1,2,3};target=3;此时我们第一次  left=0, right=2, mid=1;

发现 t a r g e t > n u m s [ m i d ] target>nums[mid] target>nums[mid],因此根据程序执行 l e f t = m i d + 1 = 2 left=mid+1=2 left=mid+1=2,此时满足 l e f t = r i g t h left=rigth left=rigth循环结束,返回值为 -1 表示没有找到,可是 3 明明在数组中。由此可见,去掉等号相当于将候选集从 [ l e f t , r i g h t ] [left,right] [left,right]变成了 ( l e f t , r i g h t ) (left,right) (left,right)

2.二分查找:第一次出现和最后一次出现的位置

通过上述努力,我们可以判断一个数字是否在数组里了,并且可以返回它的索引。然而如果数组中有多个 t a r g e t target target并且我想让你返回第一个或者最后一个索引,又该怎么寻找呢,当然你可以一个一个的往左或者往右寻找,那我们二分查找的意义就没了。由此,我们对他进行修改,下面来看具体的代码
代码如下:
寻找第一次出现的下标

    //寻找第一次出现的下标            搜索左边界
    public static int testfirst(int[] nums,int k){
        int left=0;
        int right= nums.length-1;
        while(left<=right){
            int mid=left+(right-left)/2;
            if(k==nums[mid]){					//只有这个if内容修改了
                if(left==right)					//不加这句话,会陷入死循(rigth=mid=left)
                    return left;				//left和right一样
                right=mid;						//right=mid;
            }
            else if(k<nums[mid])
                right=mid-1;
            else if(k>nums[mid])
                left = mid + 1;
        }
        return -1;
    }

寻找最后一次出现的下标

    public static int testlast(int[] nums,int k){
        int left=0;
        int right= nums.length-1;
        while(left<=right){
            int mid=(left+right+1)/2;	//思考???
            if(k==nums[mid]){
                if(left==right)
                    return right;
                left=mid;
            }
            else if(k<nums[mid])
                right=mid-1;
            else if(k>nums[mid])
                left = mid + 1;
        }
        return -1;
    }

搜索左边界的时候, r i g h t = m i d right=mid right=mid,那么搜索右边界的时候岂不是只需要写成 l e f t = m i d left=mid left=mid就可以了?结果发现程序死循环,没错这个傻子就是我。

可以看到我把 m i d = l e f t + ( r i g h t − l e f t ) / 2 ; mid=left+(right-left)/2; mid=left+(rightleft)/2;修改成了 m i d = ( l e f t + r i g h t + 1 ) / 2 ; mid=(left+right+1)/2; mid=(left+right+1)/2;
可能有的朋友要反问我了,难道左右边界不是对称的吗?为什么右边界要写成这样?
因为我们的 i n t int int型除法得到的是商。举个例子:
n u m s [ 8 ] nums[8] nums[8]数组里面放了8个数字1

l e f t = 0 , r i g h t = 7 left=0,right=7 left=0,right=7我们发现 m i d = 3 − > l e f t = 3 − > m i d = 5 − > l e f t = 6 > m i d = 6 − > l e f t = 6 mid=3 ->left=3->mid=5->left=6>mid=6->left=6 mid=3>left=3>mid=5>left=6>mid=6>left=6

我们发现 l e f t < r i g h t left<right left<right永远成立。程序陷入死循环!

时间复杂度: f ( n ) = O ( l o g N ) f(n)=O(log N) f(n)=O(logN)

很多时候题目里出现要求时间复杂度为 f ( n ) = O ( l o g N ) f(n)=O(log N) f(n)=O(logN),其实即是对小伙伴的要求,也是一种提示,是否可以使用二分法求解呢?
以上就是今天分享内容,本文仅仅作为自己学习的总结,为同样在学习的小伙伴提供参考。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

登sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值