Leetcode刷题详解——在排序数组中查找元素的第一个和最后一个位置

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

2. 题目描述:

给你一个按照非递减顺序排列的整数数组 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 <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

3. 寻找左边界的思路:

  1. x表示该元素,resLest表示左边界,resRight表示右边界
  2. 左边区间都是小于x
  3. 右边区间(包括左边界)都是大于等于x
  4. mid的落点情况如下:
    1. mid落在[left,resLeft-1]区间的时候,也就是arr[mid]<target,说明[left,mid]都是可以舍去的,此时更新leftmid+1的位置,继续在[mid+1,right]上寻找左边界
    2. mid落在[resLeft,right]的区间的时候,也就是arr[mid]>=target,说明[mid+1,right](因为mid可能是最终结果,不能舍去)是可以舍去的,此时更新rightmid的位置,继续[left,mid]上寻找左边界
  5. 由此就可以通过二分来快速寻找左边界

注意事项:找中间元素需要向下取整

因为后续继续移动指针的时候:

左指针:left=mid+1,是会向后移动的,因此区间是会缩小的

右指针:right=mid,可能会原地踏步(比如:如果向上取整的话,如果剩下12两个元素的,left==1right==2mid==2。更新区间之后,leftright,mid的值没有改变,就会陷入死循环)

4. 寻找有右边界的思路:

  1. x表示该元素,resLest表示左边界,resRight表示右边界
  2. 左边区间(包括右边界)[left,resRight]都是小于等于x
  3. 右边区间[resRight+1,right]都是大于x是的
  4. mid的落点情况如下:
    1. 当我们的mid落在[left,resRight]区间的时候,也就是arr[mid]<=target,说明[left,mid-1]mid不可以舍去,因为有可能是最终结果)都是可以舍去的,此时更新leftmid的位置
    2. mid落在[resRight+1,right]的区间的时候,也就是arr[mid]>target,说明[mid,right]内的元素是可以舍去的,此时更新rightmid-1的位置
  5. 由此就可以通过二分来快速寻找右边界

注意事项:找中间元素需要向上取整

因为后续继续移动指针的时候:

左指针: left = mid ,可能会原地踏步(⽐如:如果向下取整的话,如果剩下 1,2 两个元

素, left == 1, right == 2,mid == 1 。更新区间之后, left,right,mid 的值

没有改变,就会陷⼊死循环)。

右指针: right = mid - 1 ,是会向前移动的,因此区间是会缩⼩的;

因此⼀定要注意,当right = mid 的时候,要向下取整。

5. 算法流程:

  1. 首先判断数组是否为空,如果为空则返回{-1, -1}表示无解。
  2. 初始化左指针left为0,右指针right为数组长度减1。
  3. 通过二分查找找到目标元素的起始位置。循环条件是左指针left小于右指针right,计算中间位置mid,如果中间位置的元素小于目标元素,则目标元素可能在右半部分,更新左指针leftmid + 1。如果中间位置的元素大于等于目标元素,则目标元素可能在左半部分,更新右指针rightmid。循环结束后,左指针left指向的位置就是目标元素的起始位置。
  4. 判断数组中是否存在目标元素,如果左指针left指向的元素不等于目标元素,则不存在目标元素,返回{-1, -1}表示无解。
  5. 将目标元素的起始位置保存在变量begin中。
  6. 通过二分查找找到目标元素的结束位置。重新初始化左指针left为0,右指针right为数组长度减1。循环条件是左指针left小于右指针right,计算中间位置mid,如果中间位置的元素大于目标元素,则目标元素可能在左半部分,更新右指针rightmid - 1。如果中间位置的元素小于等于目标元素,则目标元素可能在右半部分,更新左指针leftmid。循环结束后,右指针right指向的位置就是目标元素的结束位置。
  7. 返回包含起始位置和结束位置的结果数组{begin, right}作为最终答案。

6. C++算法代码:

c++ 复制代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        //处理边界情况
        if(nums.size()==0) return {-1,-1};
        int begin=0;
        //二分左端点
        int left=0,right=nums.size()-1;
        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};
        else begin=left;

        //二分右端点
        left=0,right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left+1)/2;
            if(nums[mid]>target)  right=mid-1;
            else left=mid;
        }
        return {begin,right};
    }
};
相关推荐
智者知已应修善业2 分钟前
【51单片机8位数码管同时倒计时从9999】2024-1-25
c++·经验分享·笔记·算法·51单片机
洛水水5 分钟前
【力扣100题】86.柱状图中最大的矩形
算法·leetcode·职场和发展
渡之12 分钟前
GRiM-Net 深度解析 | 无人机 GNSS 拒止场景下两阶段跨视角视觉定位框架
深度学习·算法·动态规划·无人机
测试仪器廖生1359025638531 分钟前
罗德与施瓦茨 FSP13频谱分析仪FSP30
网络·人工智能·算法
happymaker062634 分钟前
LeetCodeHot100——560.和为K的子数组
算法
dtq04241 小时前
C语言刷题数组5,6(求平均值,求最大值)
c语言·数据结构·算法
郭梧悠1 小时前
Hash算法入门Hash冲突解决方案
算法·哈希算法
洛水水2 小时前
【力扣100题】81.寻找两个正序数组的中位数
数据结构·算法·leetcode
happymaker06262 小时前
LeetCodeHot100——155.最小栈
算法
洛水水2 小时前
【力扣100题】85.每日温度
算法·leetcode·职场和发展