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};
    }
};
相关推荐
陈壮实的搬砖日记37 分钟前
一文看懂SE(Squeeze and Excitation)模块及代码实现
人工智能·深度学习·算法
Mr__vantasy1 小时前
数据结构(初阶7)---七大排序法(堆排序,快速排序,归并排序,希尔排序,冒泡排序,选择排序,插入排序)(详解)
c语言·开发语言·数据结构·算法·排序算法
♡喜欢做梦1 小时前
【Java】二叉树:数据海洋中灯塔式结构探秘(下:基本操作)
java·数据结构·算法
花糖纸木1 小时前
算法练习:34. 在排序数组中查找元素的第一个和最后一个位置
数据结构·c++·算法·leetcode
daily_23332 小时前
数据结构——排序算法第二幕(交换排序:冒泡排序、快速排序(三种版本) 归并排序:归并排序(分治))超详细!!!!
数据结构·算法·排序算法
lix的小鱼2 小时前
scala之全文单词统计
java·开发语言·后端·python·算法·c#·scala
腊笔不小新xingo2 小时前
C语言中使用动态内存
c语言·开发语言·算法
向前看-2 小时前
贪心-小c点菜问题
数据结构·c++·笔记·算法·贪心算法
ZZZ_O^O3 小时前
【贪心算法第七弹——674.最长连续递增序列(easy)】
c++·学习·算法·leetcode·贪心算法
OTWOL3 小时前
qsort函数详解+代码展示
c语言·开发语言·c++·算法