当前位置: 首页 > article >正文

二分查找_在排序数组中查找元素的第一个和最后一个位置

1.朴素二分查找

.二分查找

二分查找思路:
1.left=0,right=nums.size()-1(最后一个元素下标),取中间元素下标 mid=left+(right-left)/2 (防溢出)

2.nums[mid]>target ,说明mid右边的元素都大于target,就不用再从mid右边查找。right=mid-1

3.nums[mid]<target ,说明mid左边的元素都小于target,就不用再从mid左边查找。

left=mid+1

4.nums[mid]==tartget,找到目标值返回。

5.循环条件 left<=right,当left==right时,意味着只剩最后一个元素,有可能是目标值。

不断循环直到找到目标结果,或者left>right不满足循环条件。

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

2.查找区间左端点/右端点

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

这道题如果用朴素二分查找,虽然可以找到target==8的元素,但不一定就是目标值。

找到目标值再向左 向又查找不行吗?可以,但效率会降低。我们有其它方法可以更好的解决。

1.查找区间左端点

该方法对于朴素二分法不同的是,朴素二分法把区间分为3份,>target ==target <target。

查找区间左端点把区间分为2份,<target >=target(默认非递减)。

eg1.[5 7 7]  [8 8 10] 而目标值就在右区间的最左边。

1.如果mid落在左区间,mid指向的元素肯定不是目标值,可以让left=mid+1

2.如果mid落在右区间,mid指向的元素有可能是目标值,所以让right=mid 如果-1可能会跳过目标值。

所以right只会在右区间移动,而left最远只会到右区间的最左边。所以当left==right就可以求出结果。

3.循环条件为left<right,不能是left<=right,如果等于会一直循环,left==right

mid=(left+right)/2 mid值不变,还是在右区间,right=mid right值也不变。

4.mid的取值,mid=left+(right-left) 还是向上取整 mid=left+(right-left+1)?有什么区别?

如果只剩两个数[5 7 7]  [8 8 10] 的7 8。left指向7 right指向8,mid=left+(right-left+1) mid=1

此时mid指向8,落在右区间,right=mid。再取mid还是指向8,就会无限循环。

所以mid=left+(right-left) mid=0取靠左边的值,指向7,落在左区间,left=mid+1。此时left==right循环结束。

下面举2种极端情况

1.数组值全>target 只有右区间,right不断移动 left==0不动.left==right指向下标0

2.数组值全<arget 只有左区间,left不断移动 right==nums.szie()-1不动.left==right指向最后一个元素(因为mid=left+(right-left) mid=0取靠左边的值 不会出现mid==right left=mid+1越界的情况

 2.查找区间右端点

和查找左端点本质一样,但细节不同。

查找区间右端点把区间分为2份,<=target >target(默认非递减)。

eg1.[5 7 7 8 8] [ 10 ] 而目标值就在左区间的最右边。

1.如果mid落在右区间,mid指向的元素肯定不是目标值,可以让right=mid-1 

2.如果mid落在左区间,mid指向的元素可能是目标值,所以让left=mid 

所以left只会在左区间移动,而right最远只会到左区间的最右边。所以当left==right就可以求出结果。

3.循环条件为left<right,不能是left<=right,如果等于会一直循环,left==right

mid=(left+right)/2 mid值不变,还是在左区间,left=mid left值也不变。

4.mid的取值,mid=left+(right-left) 还是向上取整 mid=left+(right-left+1)?有什么区别?

如果只剩两个数[5 7 7 8 8 ][10] 的8 10。left指向8 right指向10,mid=left+(right-left) mid=0取靠左边的值 此时mid指向8,落在左区间,left=mid。再取mid还是指向8,就会无限循环。

所以mid=left+(right-left+1) mid=1取靠右边的值,指向10,落在右区间,right=mid-1。此时left==right循环结束。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int le=-1,ri=-1;
        int left=0,right=nums.size()-1,mid=0;
        //处理边界情况
        if(nums.empty()) return {-1,-1};
        //取左端点下标
        while(left<right)
        {
            mid=left+(right-left)/2;//取靠左边的值
            if(nums[mid]<target)
              left=mid+1;
            else
              right=mid;
        }
        le=left;
        left=0,right=nums.size()-1;
        //取右端点下标
        while(left<right)
        {
            mid=left+(right-left+1)/2;//取靠右边的值
            if(nums[mid]<=target)
              left=mid;
            else
              right=mid-1;
        }
        ri=left;
        if(nums[left]==target) return {le,ri};
        else return {-1,-1}; //没找到的情况
    }
};

1.如果目标值是左端点,就把它套在右区间[5 7 7]  [8 8 10],让左边的+1来找,mid就要取靠左的值

2.如果目标值是右端点,就把它套在左区间[5 7 7 8 8 ][10],让右边的-1来找,mid就要取靠右的值


http://www.kler.cn/news/365156.html

相关文章:

  • ResNet-RS 乳腺癌识别
  • 线程本地变量-ThreadLocal
  • 【随便聊聊】MySQL数据类型详解:从基础到高级应用
  • ASP.NET Core 路由规则 MapControllerRoute、MapDefaultControllerRoute、MapController
  • 第三十一篇:TCP协议如何解决丢包的问题,TCP系列六
  • Docker入门之构建
  • 测试WIFI和以太网的TCP带宽、UDP带宽和丢包率、延时
  • 揭开C++ STL的神秘面纱之string:提升编程效率的秘密武器
  • 没错,Go 语言的函数参数没有引用传递方式
  • 考研读研生存指南,注意事项
  • useEffect简单介绍
  • java -jar启动 报错: Error: Unable to access jarfile
  • NVR小程序接入平台/设备EasyNVR多品牌NVR管理工具/设备的多维拓展与灵活应用
  • 基于SSM+微信小程序的家政服务管理系统(家政2)
  • 批量归一化(Batch Normalization)
  • 混个1024勋章
  • [笔记] 关于CreateProcessWithLogonW函数创建进程
  • Linux系统
  • 鸿蒙到底是不是纯血?到底能不能走向世界?
  • 蓝桥杯题目理解
  • Python爬虫:urllib_ajax的get请求豆瓣电影前十页(08)
  • 【C++】用哈希桶模拟实现unordered_set和unordered_map
  • 网络安全中的日志审计:为何至关重要?
  • 35.第二阶段x86游戏实战2-C++遍历技能
  • CPRI与eCPRI的区别
  • 每天5分钟玩转C#/.NET之C#语言详细介绍