【优选算法】简单却又恶心的二分

目录

题目链接:在排序数组中查找元素的第一个和最后一个位置

解析

左边第一个

右边第一个

重点细节

由题目总结出来的模板

参考答案


题目链接:在排序数组中查找元素的第一个和最后一个位置

二分算法虽然是logN的算法,但是在很多人眼中却不值一提,觉得它简单而且只能应用在有序数组中,但是自己写的时候却经常死循环,并且有的题在不是有序的时候也是可以使用二分的,本篇将以此题为例带你写出永无bug的二分算法。

对于最基本的二分法和二分的时间复杂度就不赘述了

解析

左边第一个

我们首先以上图为例,为了找到左边第一个数字,可以将数组分为两个部分——一部分小于t,一部分大于等于t

为了防止溢出,先将mid写成mid=left+(right-left)/2,(区间长度)这里视左右情况会有一点变化,而不是mid=(right+left)/2(会有溢出风险)

当mid落到左区间的时候,mid左边包括mid都不是所要的,所以left=mid+1,在新的区间[left,right]中继续寻找

当mid落到右区间的时候,可能此时mid的值已经是3了,但是不能返回,因为要找的是最左边的一个3。那么right=mid-1吗?当mid为最左边的那个3的时候,你会发现这样写就会跳过了要的答案,为了防止这种情况,right应该写成right=mid;

右边第一个

为了找到最右边的第一个数字,我们举一反三,继续将数组分为两个情况

 将数组分为小于等于t和大于t的部分

当mid落到左区间的时候,可能mid值等于target但是我们要找到的是最右边的target

和上面一样,为了防止mid此时是最右边的target,但是写成left=mid+1错过正确答案,所以left应该写成left=mid

当mid落到右区间的时候,该区间值都是大于target的,没有我们所需要的,所以right=mid-1

但是这里的mid应该写成mid=left+(right-left+1)/2

理由:如果没有+1的话,如果出现上面这种情况,mid永远都是指向第一个,left=mid,left也一直是第一个,left永远无法等于right,造成死循环

重点细节

细节问题

1.循环结束的条件

这里应该写成while(left<right)而不是while(left<=right),当写成后面那种写法的时候你会发现死循环了,因为上面的思考都是为了让left和right最后相等,相等的时候即为所求

2.求中点的方式

        一.求左边第一个:mid=left+(right-left)/2

         二.求右边第一个:mid=left+(right-left+1)/2

由题目总结出来的模板

下面绿字为mid的写法的记忆方法(重点)

关于left和right的写法其实只要理解了上文,以后写的时候画个图即可 

参考答案

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        //数组为空
        if(nums.size()==0)
        return {-1,-1};

        int left=0,right=nums.size()-1;
        int begin=0;
        //找左边第一个
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target) left=mid+1;
            else right=mid;
        }
        //没有直接返回
        if(nums[left]!=target) return {-1,-1};
        begin=left;
        right=nums.size()-1;
        //找右边第一个
        while(left<right)
        {
            int mid=left+(right-left+1)/2;
            if(nums[mid]<=target) left=mid;
            else right=mid-1;
        }
        return {begin,right};
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值