模型总结:二分查找

一、最基本的二分查找

[704. 二分查找]

通过对基本的二分查找的分析,我们可以总结变式的来源。

1.二分查找的应用场景

1.数组是有序的。

2.数组中的元素不重复。

所谓查找,==本质就是每一次排除一批数据==。因此往往也伴随着需要分类讨论。

2.二分查找核心书写思路

  • 首先需要处理要查找的元素在数组范围外的部分,即没有找到。

  • 然后在数组中进行二分查找。

  • [left,right]:区间为left和right,其中它们都是数组下标

  • while(left<=right):这是因为下标为left=right的这个数据还没有参与比较。

  • if(target<mid) left=mid+1:这是因为mid值已经参与比较了,而且使用的是闭区间。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(target<nums[0]||target>nums[nums.size()-1])
        {
            return -1;
        }
        else
        {
            int left=0;
            int right=nums.size()-1;
            while(left<=right)
            {
                int mid=(left+right)/2;
                if(target<nums[mid])
                {
                    right=mid-1;
                }
                else if(target>nums[mid])
                {
                    left=mid+1;
                }
                else
                {
                    return mid;
                }
            }
            return -1;
        }
    }
};

二、变式一:元素可以重复

34. 在排序数组中查找元素的第一个和最后一个位置

1.思路

该变式没有完全改变有序的特性,依然是进行查找,因此还是可以使用二分查找的。

  • 还是进行分类讨论,即先处理在数组之外的情况。

  • 再在数组内部进行二分查找寻找左右边界。

    寻找边界的关键在于,当每一次比较后的left和right如何进行处理。

    这里以左边界为例:

    当target<nums[mid]的时候,显然无论元素是否重复在mid的右边都没有target值,因此right=mid-1;

    当target>nums[mid]的时候,显然无论元素是否有重复mid的左边都没有target值,因此left=mid+1;

    当target=nums[mid]的时候,由于是寻找左边界,因此不关心mid右边是否有重复的值,但是左边依然有可能有重复的值,因此right=mid-1。

    而最终while循环终止的时候,一定是right=left-1,所以right+1即left,就是左边界。

2.具体实现

class Solution {
public:
    int FindLeftBorder(vector<int>& nums,int target,int left,int right)
    {
        while(left<=right)
        {
            int mid=(right+left)/2;
            if(target>nums[mid])
            {
                left=mid+1;
            }
            else
            {
                right=mid-1;
            }
        }
        if(nums[right+1]==target)
        {
            return right+1;
        }
        else
        {
            return -1;
        }
    }
    int FindRightBorder(vector<int>& nums,int target,int left,int right)
    {
        while(left<=right)
        {
            int mid=(right+left)/2;
            if(target<nums[mid])
            {
                right=mid-1;
            }
            else
            {
                left=mid+1;
            }
        }
        if(nums[left-1]==target)
        {
            return left-1;
        }
        else
        {
            return -1;
        }
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> v;
        v.resize(2);
        if(nums.size()==0)
        {
            v[0]=-1;
            v[1]=-1;
            return v;
        }
        if(target<nums[0])
        {
            v[0]=-1;
            v[1]=-1;
            return v;
        }
        else if(target>=nums[0]&&target<=nums[nums.size()-1])
        {
            int left=0;
            int right=nums.size()-1;
            int rightborder=FindRightBorder(nums,target,left,right);
            int leftborder=FindLeftBorder(nums,target,left,right);
            v[0]=leftborder;
            v[1]=rightborder;
            return v;
        }
        else
        {
            v[0]=-1;
            v[1]=-1;
            return v;
        }
    }
};

3.思考

关键点就在于二分查找的最后结果是落在left=right的情况下,查找边界与简单的二分查找的区别就在于如何处理target=nums[mid]使得当最终left=right的时候,left或者right是一个边界。显然每一次都是对mid+1或者-1操作来缩小查找范围,可以从这里进行思考怎么处理。

三、变式二:非数组二分查找

69. x 的平方根

1.思路

思路主要在于,怎么识别它是一个二分查找。其实还是满足以上两个条件,即无重复元素,数组有序。

求x的平方根,即在x之前的数据中寻找x的平方根。

2.实现

class Solution {
public:
    int mySqrt(int x) {
        int left=1;
        int right=x;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            if(mid<(x/mid))
            {
                if(mid+1>(x/(mid+1)))
                {
                    return mid;
                }
                left=mid+1;
            }
            else if(mid>(x/mid))
            {
                if((mid-1)<(x/(mid-1)))
                {
                    return mid-1;
                }
                right=mid-1;
            }
            else
            {
                return mid;
            }
        }
        return 0;
    }
};

注意,这里要使用除法而不能是乘法,有可能出现越界的情况。同时为了避免越界,使用的是left+(right-left)/2的方式来寻找mid。

四、小结

识别二分查找:元素有序,进行查找

处理二分查找:明确结束循环的两种方式,即找到了或者left=right+1等去情况,查找元素使用前者,查找边界使用后者。

思考:通过处理当target=nums[mid]的时候的left和right值来进行切入。

本文将持续更新

全部评论

相关推荐

避坑恶心到我了大家好,今天我想跟大家聊聊我在成都千子成智能科技有限公司(以下简称千子成)的求职经历,希望能给大家一些参考。千子成的母公司是“同创主悦”,主要经营各种产品,比如菜刀、POS机、电话卡等等。听起来是不是有点像地推销售公司?没错,就是那种类型的公司。我当时刚毕业,急需一份临时工作,所以在BOSS上看到了千子成的招聘信息。他们承诺无责底薪5000元,还包住宿,这吸引了我。面试的时候,HR也说了同样的话,感觉挺靠谱的。于是,我满怀期待地等待结果。结果出来后,我通过了面试,第二天就收到了试岗通知。试岗的内容就是地推销售,公司划定一个区域,然后你就得见人就问,问店铺、问路人,一直问到他们有意向为止。如果他们有兴趣,你就得摇同事帮忙推动,促进成交。说说一天的工作安排吧。工作时间是从早上8:30到晚上18:30。早上7点有人叫你起床,收拾后去公司,然后唱歌跳舞(销售公司都这样),7:55早课(类似宣誓),8:05同事间联系销售话术,8:15分享销售技巧,8:30经理训话。9:20左右从公司下市场,公交、地铁、自行车自费。到了市场大概10点左右,开始地推工作。中午吃饭时间大约是12:00,公司附近的路边盖饭面馆店自费AA,吃饭时间大约40分钟左右。吃完饭后继续地推工作,没有所谓的固定中午午休时间。下午6点下班后返回公司,不能直接下班,需要与同事交流话术,经理讲话洗脑。正常情况下9点下班。整个上班的一天中,早上到公司就是站着的,到晚上下班前都是站着。每天步数2万步以上。公司员工没有自己的工位,百来号人挤在一个20平方米的空间里听经理洗脑。白天就在市场上奔波,公司的投入成本几乎只有租金和工资,没有中央空调。早上2小时,晚上加班2小时,纯蒸桑拿。没有任何福利,节假日也没有3倍工资之类的。偶尔会有冲的酸梅汤和西瓜什么的。公司的晋升路径也很有意思:新人—组长—领队—主管—副经理—经理。要求是业绩和团队人数,类似传销模式,把人留下来。新人不能加微信、不能吐槽公司、不能有负面情绪、不能谈恋爱、不能说累。在公司没有任何坐的地方,不能依墙而坐。早上吃早饭在公司外面的安全通道,未到上班时间还会让你吃快些不能磨蹭。总之就是想榨干你。复试的时候,带你的师傅会给你营造一个钱多事少离家近的工作氛围,吹嘘工资有多高、还能吹自己毕业于好大学。然后让你早点来公司、无偿加班、抓住你可能不会走的心思进一步压榨你。总之,大家在找工作的时候一定要擦亮眼睛,避免踩坑!———来自网友
qq乃乃好喝到咩噗茶:不要做没有专业门槛的工作
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务