每日一题——二分查找

  • Post author:
  • Post category:其他




学习目标:


每天睡前是否感到浑浑噩噩,一天又在不知不觉中过去,回想我今天都干了什么呢?


啊~我这一天又什么也没干,好有罪恶感啊,不行,我明天一定要好好学算法(手动狗头)。


明日复明日,明日何其多?不要等明天啦,和小编一起,每天睡前一道算法题,不仅解决你一天的空虚,更能助你安心入眠,远离熬夜。还能学到一点算法知识。不要小看这些知识哦,不积跬步无以至千里,不积小流无以成江海。每位大佬都不是一夜成名,都是从小白做起,日积月累,终成大佬,和小编一起,每日一题,走向大佬之路吧!




学习内容:

我们的题基本都是一些较为基础,适合日(睡)常(前)看的题,不会让你一眼就看出答案,是稍稍一点脚就能摸到头绪,但是在想的过程中又一时不知如何实现,再一想,又柳暗花明,最终跟着小编的思路一起解决问题,有不同思路可以发在评论区,一起讨论。“你若在,我必回”。


我们今天的题目是一道二分查找的问题,在算法优化中,我们常用的一种思想就是二分思想,我们今天不仅是讲解二分查找的题目,还有对于二分思想的理解。我们从今天的题目入手,以讲题目为例讲解二分的思想。看下题目:

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。


进阶:


你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?


示例 1:

输入:nums = [5,7,7,8,8,10], target = 8

输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6

输出:[-1,-1]

示例 3:

输入:nums = [], target = 0

输出:[-1,-1]

提示:

0 <= nums.length <= 10^5

-109 <= nums[i] <= 109

nums 是一个非递减数组

-109 <= target <= 109

看完题目后,我们知道是让我们

找到target在数组中的起始坐标和终点坐标

。如果用正常思想很容易就是一个for循环就可以解决的问题,但是我们算法的目的就是不断优化,刚才简单的方法时间复杂度是O(n),我们能否找到一个时间复杂的更优的方法呢?

答案是肯定的。我们看下题目还有那些信息没用上?是的,数组是非递减的,也就是说他是有序的,而我们的二分也正是基于这一点,二分的思想主要体现在对有序数组的不断缩小范围,当第i个数大于target,那么我们要找到数就一定在第i个以前,从而将搜索范围缩小到0~i,而

二分也就是不断将数据折半,从而快速减少数据量

回到我们的题目,因为数组有序,我们要找的就是target第一次出现的坐标和最后一次出现的坐标,那么我们是不是可以分别去求,用一个函数去找头,另一个去找尾。而且方法都是相同的,只是判断条件有所不同。以找头为例:

int findl(vector<int>& nums, int target,int l,int r){
    if(l>=r && nums[l]!=target) return -1;
    int mid=(l+r)/2;
    if(nums[mid]==target && (mid==0 || nums[mid-1]!=target)) return mid;
    else if(nums[mid]>=target) r=mid-1;
    else if(nums[mid]<target) l=mid+1;
    return findl(nums,target,l,r);
}

nums是数组,target是目标值,l,r是搜索区间。

当我们找到了mid,使nums[mid]=target,什么时候它使开始的坐标,那就是前一个数!=target或者坐标为0,那么此时这个mid就是我们要找的那个值;如果nums[mid]>target,也就说明我们要找的数在mid以前,我们就可以缩小区间,令r=mid-1;如果nums[mid]<target,也就说明我们要找的数在mid以后,我们就可以缩小区间,令l=mid+1;还有一种情况就是nums[mid]=target但不满足前一个数!=target或者坐标为0的条件,那这个应该怎么缩小区间呢?这就要看我们的目的是什么了?因为是要找到头,所以我们要将搜索区间向前移动,令r=mid-1。然后进行递归寻找。

然后找尾的方法和找头近乎相同,就是判断条件改一下,这就交给你来完成啦!自己手动一边比看的效果要强很多的。点击下方链接可以自己做一遍这道题,会有更多收获的!




例题


icon-default.png?t=M1FB
https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/



最后就是我们函数的整体:

class Solution {
public:
int findl(vector<int>& nums, int target,int l,int r){
    if(l>=r && nums[l]!=target) return -1;
    int mid=(l+r)/2;
    if(nums[mid]==target && (mid==0 || nums[mid-1]!=target)) return mid;
    else if(nums[mid]>=target) r=mid-1;
    else if(nums[mid]<target) l=mid+1;
    return findl(nums,target,l,r);
}
int findr(vector<int>& nums, int target,int l,int r){
    if(l>=r && nums[l]!=target) return -1;
    int mid=(l+r)/2;
    if(nums[mid]==target && (mid==nums.size()-1 || nums[mid+1]!=target)) return mid;
    else if(nums[mid]>target) r=mid-1;
    else if(nums[mid]<=target) l=mid+1;
    return findr(nums,target,l,r);
}
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res(2,-1);
        if(nums.size()==0) return res;
        int r=nums.size()-1;
        res[0]=findl(nums,target,0,r);
        res[1]=findr(nums,target,0,r);
        return res;
    }
};


注意搜索不到的判断条件和返回值


每天

坚持

是一件很累的事,即使很慢,但也不要停止。


大家记得

点赞收藏

,防止找不到哦。

欢迎大家

订阅

小编的每日一题专栏,会每天更新,都是用心准备的哦!

若有不同思路,欢迎评论区留言,看到必回,Goodnight!



版权声明:本文为Cooler_z原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。