【代码随想录刷题记录】LeetCode34在排序数组中查找元素的第一个和最后一个位置

news2024/12/23 15:27:07

题目地址
最近忙活实验,实在没空刷题,这个题对我来说难度还蛮大的,尤其是理解那个找左边界和找右边界的条件,后来我按照自己的理解写了出来(感觉给的答案解释起来有点反认识规律),所以我从0开始推导一下这个题的解法,算法解析写的不详细的文章我实在看不懂,没时间去再看一遍稿子里有没有打错字的地方,如果有错误,欢迎评论区指出(大概没空回来改)

1.思路

1.1 三种基本情况

我们注意到,这个问题无非三种情况,就如卡尔老师说的(直接引用一下哈哈哈,偷懒):

寻找target在数组里的左右边界,有如下三种情况:

  • 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4,
    5},target为6,此时应该返回{-1, -1}
  • 情况二:target在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
  • 情况三:target在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
    这三种情况都考虑到,说明就想的很清楚了。

1.2 基本思路分析

题目要求是 O ( l o g ( n ) ) O(log(n)) O(log(n))的复杂度,肯定要魔改二分查找,先来回顾一下二分查找(以左闭右闭为例),假设有如下这样一个升序(可以包括多个重复的值)数组nums,其target值为3:

第1步  middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 0 2 ⌋ + 0 = 4 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-0}{2}\right\rfloor+0=4  middle =2 right  left + left =290+0=4
target = 3 = nums [ middle ] = nums [ 4 ] = 3 \text{target}=3=\text{nums}[\text{middle}]=\text{nums}[4]=3 target=3=nums[middle]=nums[4]=3
此时就直接找到了3,但是我们要找的是多个连续重复的值的开头的下标和结尾的下标,也就是找到第一个3和最后一个3的下标(即{2,6}),我们就得想办法找到它的边界,也就是我们要想方设法让middle继续向左移动找到左边界,然后还要让middle向右移动找到右边界,回顾一下二分查找(左闭右闭)的代码:

class Solution {
public:
    //左闭右闭
    int search(vector<int>& nums, int target) {
        int left = 0; //最左侧下标
        int right = nums.size() - 1; //最右侧下标
        /*注意!!!求解中点是不断地求解,不是求解一次就完成了,
        我一开始把求解中点写到循环外了,这是错误的*/
        //(错误)int middle = ((right - left) >> 1) + left; //求解中点下标
        // 两边都能取到,所以用小于等于
        while(left <= right)
        {
            // 中点的判断条件是right - left(区间长度)除2(相当于右移1位)再加left(因为区间不一定是从0开始的)
            int middle = ((right - left) >> 1) + left; //求解中点下标
            //如果中间值就是target,直接返回中间下标
            if(target == nums[middle])
            {
                return middle;
            }
            //如果target大于中间值,向右子列寻找,则left变成middle+1
            else if(target > nums[middle])
            {
                left = middle + 1;
            }
            //如果target小于中间值,向左子列寻找,则right变为middle-1(因为是左闭右闭,闭合包括区间端点,所以需要抠除middle)
            else
            {
                right = middle - 1;
            }
        }
        //都过一遍没找到,返回-1
        return -1;
    }
};

这段代码的核心功能就是这个循环:

        while(left <= right)
        {
            // 中点的判断条件是right - left(区间长度)除2(相当于右移1位)再加left(因为区间不一定是从0开始的)
            int middle = ((right - left) >> 1) + left; //求解中点下标
            //如果中间值就是target,直接返回中间下标
            if(target == nums[middle])
            {
                return middle;
            }
            //如果target大于中间值,向右子列寻找,则left变成middle+1
            else if(target > nums[middle])
            {
                left = middle + 1;
            }
            //如果target小于中间值,向左子列寻找,则right变为middle-1(因为是左闭右闭,闭合包括区间端点,所以需要抠除middle)
            else
            {
                right = middle - 1;
            }
        }

1.3 探究找右边界

如果我想让middle继续向左右两侧移动,那target == nums[middle]这个条件必须融到target > nums[middle]或者target < nums[middle]这两个条件中(取并集),原因是当target == nums[middle]时,它只能找到连续重复序列的中间位置的元素,就退出循环了,为了不让它退出循环继续让middle移动,所以我们要重新审视一下if else的条件,如果target == nums[middle]与target > nums[middle]做并集得到target >= nums[middle]这样的条件,那么重新来一遍这个查找的过程:

        while(left <= right)
        {
            int middle = ((right - left) >> 1) + left; //求解中点下标
			if(target >= nums[middle])
            {
                left = middle + 1;
            }
            else
            {
                right = middle - 1;
            }
        }

1.3.1 target在数组范围中,且数组中存在target

我开头举的例子恰好是“target在数组范围中,且数组中存在target”的情况,下面开始迭代:
第1步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 0 2 ⌋ + 0 = 4 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-0}{2}\right\rfloor+0=4  middle =2 right  left + left =290+0=4

target = 3 = nums [ middle ] = nums [ 4 ] = 3 \text{target}=3=\text{nums}[\text{middle}]=\text{nums}[4]=3 target=3=nums[middle]=nums[4]=3,故 left = middle + 1 = 4 + 1 = 5 < right = 9 \text{left}=\text{middle}+1=4+1=5<\text{right}=9 left=middle+1=4+1=5<right=9,满足循环条件,继续循环

第2步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 5 2 ⌋ + 5 = 7 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-5}{2}\right\rfloor+5=7  middle =2 right  left + left =295+5=7

target = 3 < nums [ middle ] = nums [ 7 ] = 4 \text{target}=3<\text{nums}[\text{middle}]=\text{nums}[7]=4 target=3<nums[middle]=nums[7]=4,故 right = middle − 1 = 7 − 1 = 6 > left = 5 \text{right}=\text{middle}-1=7-1=6>\text{left}=5 right=middle1=71=6>left=5,满足循环条件,继续循环

第3步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 6 − 5 2 ⌋ + 5 = 5 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{6-5}{2}\right\rfloor+5=5  middle =2 right  left + left =265+5=5

target = 3 = nums [ middle ] = nums [ 5 ] = 3 \text{target}=3=\text{nums}[\text{middle}]=\text{nums}[5]=3 target=3=nums[middle]=nums[5]=3,故 left = middle + 1 = 5 + 1 = 6 = right = 6 \text{left}=\text{middle}+1=5+1=6=\text{right}=6 left=middle+1=5+1=6=right=6,满足循环条件,继续循环

到这里,我们发现,找到了右边界,但是循环还没停,继续下一步:
第4步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 6 − 6 2 ⌋ + 6 = 6 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{6-6}{2}\right\rfloor+6=6  middle =2 right  left + left =266+6=6

target = 3 = nums [ middle ] = nums [ 6 ] = 3 \text{target}=3=\text{nums}[\text{middle}]=\text{nums}[6]=3 target=3=nums[middle]=nums[6]=3,故 left = middle + 1 = 6 + 1 = 7 > right = 6 \text{left}=\text{middle}+1=6+1=7>\text{right}=6 left=middle+1=6+1=7>right=6,不满足循环条件,退出循环,最后right,middle和left三个值对应的下标是这样的:

我们看到,右边界的下标可能是right或者middle或者是left-1,但是注意,结束循环后直接在循环外返回right,left或者提前定义middle,然后循环外返回middle可能都有问题,原因是我们最后还有找不到返回特定值(如-1或者-2,只要不是0开始的自然数,也就是下标的合法值,其余情况都行),这个题没让我们返回没找到的元素应插入的位置(相关文章),所以不能直接返回right和middle,只能在循环外再定义一个变量,因为我们探究到这种情况是找到右边界,所以我们将变量声明为int rightBorder = -1(一开始就定义好数组为空的情况),然后我们要想办法让rightBorder这个变量跟随着循环迭代更新,但是究竟要记录right还是left-1还是middle?这里就要考虑另外两种特殊情况,假设找不到target

1.3.2 target在数组范围中,且数组中不存在target

有如下如下这样一个升序(可以包括多个重复的值)数组nums,其target值为5:

第1步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 0 2 ⌋ + 0 = 4 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-0}{2}\right\rfloor+0=4  middle =2 right  left + left =290+0=4

target = 5 > nums [ middle ] = nums [ 4 ] = 4 \text{target}=5>\text{nums}[\text{middle}]=\text{nums}[4]=4 target=5>nums[middle]=nums[4]=4,故 left = middle + 1 = 4 + 1 = 5 < right = 9 \text{left}=\text{middle}+1=4+1=5<\text{right}=9 left=middle+1=4+1=5<right=9,满足循环条件,继续循环

第2步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 5 2 ⌋ + 5 = 7 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-5}{2}\right\rfloor+5=7  middle =2 right  left + left =295+5=7

target = 5 < nums [ middle ] = nums [ 7 ] = 7 \text{target}=5<\text{nums}[\text{middle}]=\text{nums}[7]=7 target=5<nums[middle]=nums[7]=7,故 right = middle − 1 = 7 − 1 = 6 > left = 5 \text{right}=\text{middle}-1=7-1=6>\text{left}=5 right=middle1=71=6>left=5,满足循环条件,继续循环

第3步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 6 − 5 2 ⌋ + 5 = 5 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{6-5}{2}\right\rfloor+5=5  middle =2 right  left + left =265+5=5

target = 5 < nums [ middle ] = nums [ 5 ] = 6 \text{target}=5<\text{nums}[\text{middle}]=\text{nums}[5]=6 target=5<nums[middle]=nums[5]=6,故 right = middle − 1 = 6 − 1 = 5 = left = 5 \text{right}=\text{middle}-1=6-1=5=\text{left}=5 right=middle1=61=5=left=5,满足循环条件,继续循环

第4步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 5 − 5 2 ⌋ + 5 = 5 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{5-5}{2}\right\rfloor+5=5  middle =2 right  left + left =255+5=5

target = 5 < nums [ middle ] = nums [ 5 ] = 6 \text{target}=5<\text{nums}[\text{middle}]=\text{nums}[5]=6 target=5<nums[middle]=nums[5]=6,故 right = middle − 1 = 5 − 1 = 4 < left = 5 \text{right}=\text{middle}-1=5-1=4<\text{left}=5 right=middle1=51=4<left=5,不满足循环条件,跳出循环

再加上middle的情况:

我们发现,找不到的情况下,middle并不能像二分查找那样返回-1,此时并不能直接将rightBorder设置为right,left-1和middle,看起来在找不到元素的情况下,我们还是没法直接让循环外的rightBorder变量设置为right,left-1,middle三者中的任意一个值,可能最终判断条件和找左侧边界有关,接下来看第三种情况:

1.3.3 target 在数组范围的右边或者左边

假设有如下数组nums,target为1:

第1步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 3 − 0 2 ⌋ + 0 = 1 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{3-0}{2}\right\rfloor+0=1  middle =2 right  left + left =230+0=1

target = 1 < nums [ middle ] = nums [ 1 ] = 3 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[1]=3 target=1<nums[middle]=nums[1]=3,故 right = middle − 1 = 3 − 1 = 2 > left = 0 \text{right}=\text{middle}-1=3-1=2>\text{left}=0 right=middle1=31=2>left=0,满足循环条件,继续循环

第2步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 2 − 0 2 ⌋ + 0 = 1 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{2-0}{2}\right\rfloor+0=1  middle =2 right  left + left =220+0=1

target = 1 < nums [ middle ] = nums [ 1 ] = 3 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[1]=3 target=1<nums[middle]=nums[1]=3,故 right = middle − 1 = 2 − 1 = 1 > left = 0 \text{right}=\text{middle}-1=2-1=1>\text{left}=0 right=middle1=21=1>left=0,满足循环条件,继续循环

第3步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 1 − 0 2 ⌋ + 0 = 0 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{1-0}{2}\right\rfloor+0=0  middle =2 right  left + left =210+0=0

target = 1 < nums [ middle ] = nums [ 0 ] = 2 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[0]=2 target=1<nums[middle]=nums[0]=2,故 right = middle − 1 = 1 − 1 = 0 = left = 0 \text{right}=\text{middle}-1=1-1=0=\text{left}=0 right=middle1=11=0=left=0,满足循环条件,继续循环

第4步

 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 0 − 0 2 ⌋ + 0 = 0 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{0-0}{2}\right\rfloor+0=0  middle =2 right  left + left =200+0=0


target = 1 < nums [ middle ] = nums [ 0 ] = 2 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[0]=2 target=1<nums[middle]=nums[0]=2,故 right = middle − 1 = 0 − 1 = − 1 < left = 0 \text{right}=\text{middle}-1=0-1=-1<\text{left}=0 right=middle1=01=1<left=0,不满足循环条件,跳出循环

此时的right为-1,再把middle的情况加上去:

1.3.4 找右边界总结

在1.3.1中,我们看到middle应该是和right一样的,在这里,middle是在right前面,所以rightBorder不应该被设置为middle或者middle-1,应该设置为right或者left-1,但是在1.3.2“target在数组范围中,且数组中不存在target”情况中,rightBorder本应该设置为找不到的值(-1之类),但是如果在这种情况下将rightBorder设置为right或者left-1,似乎不合理,但是我们先忘掉这种“不合理”,按照现在整理出来的思路,将找右边界的函数写出来,即:

    //找右边界
    int getRightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightBorder = -1; 
        //左闭右闭
        while (left <= right)
        {
            int middle = left + ((right - left) >> 1); //除2相当于右移1位,节省时间
            if (target >= nums[middle])
            {
                left = middle + 1;
            }
            else
            {
                right = middle - 1;
            }
            rightBorder = right; //写rightBorder = left - 1也行
        }
        return rightBorder;
    }

1.4 探究找左边界

左边界与右边界在条件上处理是类似的,如果我想让middle继续向左侧移动,那target == nums[middle]这个条件改为target <= nums[middle]这样的条件,即循环框架大概是这样的:

        while(left <= right)
        {
            int middle = ((right - left) >> 1) + left; //求解中点下标
			if(target <= nums[middle])
            {
                right = middle - 1}
            else
            {
                left = middle + 1;
            };
        }

1.4.1 target在数组范围中,且数组中存在target

假设有如下这样一个升序(可以包括多个重复的值)数组nums,其target值为3:

第1步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 0 2 ⌋ + 0 = 4 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-0}{2}\right\rfloor+0=4  middle =2 right  left + left =290+0=4

target = 3 = nums [ middle ] = nums [ 4 ] = 3 \text{target}=3=\text{nums}[\text{middle}]=\text{nums}[4]=3 target=3=nums[middle]=nums[4]=3,故 right = middle − 1 = 4 − 1 = 3 > left = 0 \text{right}=\text{middle}-1=4-1=3>\text{left}=0 right=middle1=41=3>left=0,满足循环条件,继续循环

第2步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 3 − 0 2 ⌋ + 0 = 1 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{3-0}{2}\right\rfloor+0=1  middle =2 right  left + left =230+0=1

target = 3 > nums [ middle ] = nums [ 1 ] = 2 \text{target}=3>\text{nums}[\text{middle}]=\text{nums}[1]=2 target=3>nums[middle]=nums[1]=2,故 left = middle + 1 = 1 + 1 = 2 < right = 3 \text{left}=\text{middle}+1=1+1=2<\text{right}=3 left=middle+1=1+1=2<right=3,满足循环条件,继续循环
第3步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 3 − 2 2 ⌋ + 2 = 2 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{3-2}{2}\right\rfloor+2=2  middle =2 right  left + left =232+2=2

target = 3 = nums [ middle ] = nums [ 2 ] = 3 \text{target}=3=\text{nums}[\text{middle}]=\text{nums}[2]=3 target=3=nums[middle]=nums[2]=3,故 right = middle − 1 = 3 − 1 = 2 = left = 2 \text{right}=\text{middle}-1=3-1=2=\text{left}=2 right=middle1=31=2=left=2,满足循环条件,继续循环

第4步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 2 − 2 2 ⌋ + 2 = 2 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{2-2}{2}\right\rfloor+2=2  middle =2 right  left + left =222+2=2

target = 3 = nums [ middle ] = nums [ 2 ] = 3 \text{target}=3=\text{nums}[\text{middle}]=\text{nums}[2]=3 target=3=nums[middle]=nums[2]=3,故 right = middle − 1 = 2 − 1 = 1 < left = 2 \text{right}=\text{middle}-1=2-1=1<\text{left}=2 right=middle1=21=1<left=2,不满足循环条件,跳出循环

由此可见,最后我们要效仿找有边界一样的循环外变量leftBorder应该被设置为left或者right+1,基于找右边界的逻辑,middle变动太大,不考虑。

1.4.2 target在数组范围中,且数组中不存在target

有如下如下这样一个升序数组nums,其target值为5:

第1步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 0 2 ⌋ + 0 = 4 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-0}{2}\right\rfloor+0=4  middle =2 right  left + left =290+0=4

target = 5 > nums [ middle ] = nums [ 4 ] = 4 \text{target}=5>\text{nums}[\text{middle}]=\text{nums}[4]=4 target=5>nums[middle]=nums[4]=4,故 left = middle + 1 = 4 + 1 = 5 < right = 9 \text{left}=\text{middle}+1=4+1=5<\text{right}=9 left=middle+1=4+1=5<right=9,满足循环条件,继续循环

第2步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 9 − 5 2 ⌋ + 5 = 7 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{9-5}{2}\right\rfloor+5=7  middle =2 right  left + left =295+5=7

target = 5 < nums [ middle ] = nums [ 7 ] = 7 \text{target}=5<\text{nums}[\text{middle}]=\text{nums}[7]=7 target=5<nums[middle]=nums[7]=7,故 right = middle − 1 = 7 − 1 = 6 > left = 5 \text{right}=\text{middle}-1=7-1=6>\text{left}=5 right=middle1=71=6>left=5,满足循环条件,继续循环

第3步

 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 6 − 5 2 ⌋ + 5 = 5 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{6-5}{2}\right\rfloor+5=5  middle =2 right  left + left =265+5=5

target = 5 < nums [ middle ] = nums [ 5 ] = 6 \text{target}=5<\text{nums}[\text{middle}]=\text{nums}[5]=6 target=5<nums[middle]=nums[5]=6,故 right = middle − 1 = 6 − 1 = 5 = left = 5 \text{right}=\text{middle}-1=6-1=5=\text{left}=5 right=middle1=61=5=left=5,满足循环条件,继续循环

第4步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 5 − 5 2 ⌋ + 5 = 5 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{5-5}{2}\right\rfloor+5=5  middle =2 right  left + left =255+5=5

target = 5 < nums [ middle ] = nums [ 5 ] = 6 \text{target}=5<\text{nums}[\text{middle}]=\text{nums}[5]=6 target=5<nums[middle]=nums[5]=6,故 right = middle − 1 = 5 − 1 = 4 < left = 5 \text{right}=\text{middle}-1=5-1=4<\text{left}=5 right=middle1=51=4<left=5,不满足循环条件,跳出循环

再加上middle:

由此可见,leftBorder在这种情况下,也是不知道设置成什么值比较好,但是由于找不到元素的情况不涉及target == nums[middle],所以和找右侧边界的结果是一样的,继续往下看

1.4.3 target在数组范围的右边或者左边

假设有如下数组nums,target为1:

第1步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 3 − 0 2 ⌋ + 0 = 1 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{3-0}{2}\right\rfloor+0=1  middle =2 right  left + left =230+0=1

target = 1 < nums [ middle ] = nums [ 1 ] = 3 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[1]=3 target=1<nums[middle]=nums[1]=3,故 right = middle − 1 = 3 − 1 = 2 > left = 0 \text{right}=\text{middle}-1=3-1=2>\text{left}=0 right=middle1=31=2>left=0,满足循环条件,继续循环

第2步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 2 − 0 2 ⌋ + 0 = 1 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{2-0}{2}\right\rfloor+0=1  middle =2 right  left + left =220+0=1

target = 1 < nums [ middle ] = nums [ 1 ] = 3 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[1]=3 target=1<nums[middle]=nums[1]=3,故 right = middle − 1 = 2 − 1 = 1 > left = 0 \text{right}=\text{middle}-1=2-1=1>\text{left}=0 right=middle1=21=1>left=0,满足循环条件,继续循环

第3步
 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 1 − 0 2 ⌋ + 0 = 0 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{1-0}{2}\right\rfloor+0=0  middle =2 right  left + left =210+0=0

target = 1 < nums [ middle ] = nums [ 0 ] = 2 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[0]=2 target=1<nums[middle]=nums[0]=2,故 right = middle − 1 = 1 − 1 = 0 = left = 0 \text{right}=\text{middle}-1=1-1=0=\text{left}=0 right=middle1=11=0=left=0,满足循环条件,继续循环

第4步

 middle  = ⌊  right  −  left  2 ⌋ +  left  = ⌊ 0 − 0 2 ⌋ + 0 = 0 \text { middle }=\left\lfloor\frac{\text { right }- \text { left }}{2}\right\rfloor+\text { left }=\left\lfloor\frac{0-0}{2}\right\rfloor+0=0  middle =2 right  left + left =200+0=0


target = 1 < nums [ middle ] = nums [ 0 ] = 2 \text{target}=1<\text{nums}[\text{middle}]=\text{nums}[0]=2 target=1<nums[middle]=nums[0]=2,故 right = middle − 1 = 0 − 1 = − 1 < left = 0 \text{right}=\text{middle}-1=0-1=-1<\text{left}=0 right=middle1=01=1<left=0,不满足循环条件,跳出循环

此时的right为-1,再把middle的情况加上去:

它还是和1.3.3的情况是一样的,只不过,这次我们要考量一下,leftBorder到底应该设置为什么值,如果设置为left,那么,此时leftBorder为0,如果设置为right,那么leftBorder为-1,看似是成功设置一个不合理的值,但是1.4.1的情况又不成立,所以我们先保证target在数组范围中,且数组中存在target的情况,把leftBorder设置为left(或者right + 1)

1.4.4 找左边界总结

综上所述,我们设置循环外的变量leftBorder来记录左边界的值,即:

    //找左边界
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int leftBorder = -1; 
        //左闭右闭
        while (left <= right)
        {
            int middle = left + ((right - left) >> 1); //除2相当于右移1位,节省时间
            if (target <= nums[middle])
            {
                right = middle - 1; //target在左区间
            }
            else
            {
                left = middle + 1;
            }
            leftBorder = left; // 写leftBorder = right + 1也行
        }
        return leftBorder;
    }

1.5 总结与实现

我们似乎还没有解决找不到元素时,rightBorder和leftBorder应该设置什么值,不妨把它们联合起来思考,我们已经将leftBorder设置为left(或right+1),rightBorder设置为right(或left-1),先看“target在数组范围中,且数组中不存在target”的情况(例子还是上面的),直接看最后迭代的样子:

我们可以看到leftBorder和rightBoder如果按照上述规则设置,那么他们的差应该为1(或者leftBorder大于rightBorder),leftBorder大于rightBorder恰好是不符合常理的,所以这种情况应该返回{-1, -1}
再看“target在数组范围的右边或者左边”的情况(例子还是上面的),直接看最后迭代的样子:

我们可以看到,还是上面的一样的情况,leftBorder和rightBoder如果按照上述规则设置,那么他们的差应该为1(或者leftBorder大于rightBorder),leftBorder大于rightBorder恰好也是不符合常理的,所以这种情况应该返回{-1, -1}
所以,最后,我们的代码可以写成:

class Solution {
public:
    //找右边界
    int getRightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightBorder = -1; 
        //左闭右闭
        while (left <= right)
        {
            int middle = left + ((right - left) >> 1); //除2相当于右移1位,节省时间
            if (target >= nums[middle])
            {
                left = middle + 1;
            }
            else
            {
                right = middle - 1;
            }
            rightBorder = right; //写rightBorder = left - 1也行
        }
        return rightBorder;
    }
    //找左边界
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int leftBorder = -1;
        //左闭右闭
        while (left <= right)
        {
            int middle = left + ((right - left) >> 1); //除2相当于右移1位,节省时间
            if (target <= nums[middle])
            {
                right = middle - 1; 
            }
            else
            {
                left = middle + 1;
            }
            leftBorder = left; // 写leftBorder = right + 1也行
        }
        return leftBorder;
    }
    //处理一下两种情况
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        if(leftBorder > rightBorder) // 或者写leftBorder - rightBorder == 1这个条件
        {
            return {-1, -1};
        }
        return{leftBorder, rightBorder};
    }
};

结果也是顺利通过:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1613456.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASP.NET Core 3 高级编程(第8版) 学习笔记 04

第 19 章主要介绍 Restful Service 的相关知识。Restful Service 的核心内容是&#xff1a;&#xff08;1&#xff09;HTTP 请求或 HTTP 动词&#xff0c;用 HTTP 请求表达不同的操作&#xff0c;最好遵守惯例。&#xff08;2&#xff09;资源&#xff0c;通过 PATH 结合 paylo…

[Linux][多线程][一][线程基础概念][进程VS线程][线程控制]详细讲解

目录 0.预备知识1.页表的映射2.二级页表 1.线程基础概念1.什么是线程&#xff1f;2.理解流程梳理 -- 如何理解线程&#xff1f;3.线程优点4.线程缺点5.线程异常6.线程用途 2.进程VS线程1.进程和线程2.进程和线程的资源共享3.进程和线程的关系4.关于进程线程的问题 3.线程控制1.…

机器学习(二)之监督学习

前言&#xff1a; 上一节大概讲解了几种学习方式&#xff0c;下面几张就具体来讲讲监督学习的几种算法。 以下示例中和都是权重的意思&#xff01;&#xff01;&#xff01; 注&#xff1a;本文如有错误之处&#xff0c;还请读者指出&#xff0c;欢迎评论区探讨&#xff01; 1…

解释一下“暂存区”的概念,在Git中它扮演什么角色?

文章目录 暂存区在Git中的概念与作用什么是暂存区&#xff08;Staging Area&#xff09;暂存区的位置和结构 暂存区在Git工作流程中的角色1. 分离工作区与版本库的交互示例代码与操作步骤示例1&#xff1a;将工作区的修改添加至暂存区 2. 控制提交内容的粒度示例2&#xff1a;分…

玩转Virtual Box虚拟机

玩转Virtual Box虚拟机 虚拟化技术和虚拟机简介 什么是虚拟化技术&#xff1f; 虚拟化技术是将计算机的各种硬件资源予以抽象、转换、分割、组合的一种计算机技术。虚拟化技术打破了实体结构间不可切割的障碍&#xff0c;从而使用户可以按照需求重新组合硬件资源&#xff0c…

C/C++开发,opencv-ml库学习,支持向量机(SVM)应用

一、OpenCV支持向量机&#xff08;SVM&#xff09;模块 1.1 openCV的机器学习库 OpenCV-ml库是OpenCV&#xff08;开放源代码计算机视觉库&#xff09;中的机器学习模块&#xff0c;常用于分类和回归问题&#xff0c;它是 OpenCV 众多modules下的一个模块。 该模块提供了一系列…

第15届蓝桥杯题解

A题 结果&#xff1a;2429042904288 思路很简单 前20个数分别是 20 24 40 48 60 72 80 96 100 120 140 144 160 168 180 192 200 216 220 240 第2 4 6 8 12 ...n个数分别是24的 1倍 2倍 3倍 4倍 6倍 n/2倍 所以第202420242024 个数就是 24的 101210121012倍 B题 答案&am…

十一、Yocto集成tcpdump等网络工具

文章目录 Yocto集成tcpdump等网络工具networking layer集成 Yocto集成tcpdump等网络工具 本篇文章为基于raspberrypi 4B单板的yocto实战系列的第十一篇文章&#xff1a; 一、yocto 编译raspberrypi 4B并启动 二、yocto 集成ros2(基于raspberrypi 4B) 三、Yocto创建自定义的lay…

Docker 安装 Mongo

创建宿主机目录 在你的宿主机上创建必要的目录来存储 MongoDB 的数据和配置文件。这样做可以保证即使容器被删除&#xff0c;数据也能得到保留。 mkdir -p /develop/mongo/data mkdir -p /develop/mongo/config创建 MongoDB 配置文件 创建一个名为 mongod.conf 的 MongoDB 配…

arping命令详解

arping – send ARP REQUEST to a neighbour host. arping 是一个在网络中发送 ARP 请求以查找特定 IP 地址对应的 MAC 地址的命令行工具。它的功能类似于 ping 命令&#xff0c;基于ARP协议报文的交互机制&#xff0c;只能测试同一网段或子网的网络主机的连通性。 ARP 是 Add…

【声呐仿真】学习记录1.5-使用docker配置dave(先看这个!)、解决一些问题

【声呐仿真】学习记录1.5-使用docker配置dave、解决一些问题 docker配置dave123 以下为未完全解决问题的随手记录&#xff0c;待日后解决再补充1.pcap、png解决&#xff0c;libusb未解决&#xff08;不要修改libusb相关的&#xff09;2.ISO C3.换源4.自动安装相关依赖 docker配…

【保姆级讲解下gateway基本配置】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

1、揭开程序运行的神秘面纱

要深入理解JVM技术&#xff0c;我们必须先搞清楚一个基本问题&#xff1a;我们日常编写的Java代码是如何被执行的呢&#xff1f; 让我们逐步解析这个问题。首先&#xff0c;假设我们已经编写了一些Java代码&#xff0c;这些代码通常会包含许多以“.java”为后缀的源文件&#…

《深入浅出.NET框架设计与实现》笔记2——C#源码从编写到执行的流程

中间语言&#xff08;Intermediate Language&#xff0c;IL&#xff09; C#编译器在编译时&#xff0c;会将源代码作为输入&#xff0c;并以中间语言形式输入出&#xff0c;该代码保存在*.exe文件中或*.dll文件中。 公共语言运行时&#xff08;CLR&#xff09; 可以将IL代码…

ROS机器人入门第七课:参数服务器

文章目录 ROS机器人入门第七课&#xff1a;参数服务器一、参数服务器介绍二、参数操作1.参数服务器新增(修改)参数2.参数服务器获取参数3.参数服务器删除参数 ROS机器人入门第七课&#xff1a;参数服务器 一、参数服务器介绍 参数服务器在ROS中主要用于实现不同节点之间的数据…

【第34天】SQL进阶-SQL高级技巧-Window Funtion(SQL 小虚竹)

回城传送–》《100天精通MYSQL从入门到就业》 文章目录 零、前言一、练习题目二、SQL思路初始化数据什么是Window Funtion窗口函数的分类语法结构第一种写法&#xff1a;第二种写法&#xff1a; 实战体验序号函数&#xff1a;row_number()序号函数&#xff1a;rank()序号函数&…

AI大模型量化格式介绍(GPTQ,GGML,GGUF,FP16/INT8/INT4)

在 HuggingFace 上下载模型时&#xff0c;经常会看到模型的名称会带有fp16、GPTQ&#xff0c;GGML等字样&#xff0c;对不熟悉模型量化的同学来说&#xff0c;这些字样可能会让人摸不着头脑&#xff0c;我开始也是一头雾水&#xff0c;后来通过查阅资料&#xff0c;总算有了一些…

Leetcode144_二叉树的前序遍历

1.leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2.题目描述 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#xff1a; 输入&#xf…

如何在PostgreSQL中使用CTE(公共表表达式)来简化复杂的查询逻辑?

文章目录 解决方案步骤示例代码 结论 在处理复杂的SQL查询时&#xff0c;我们经常会遇到需要多次引用子查询或中间结果的情况。这可能会使得查询变得冗长且难以理解。为了解决这个问题&#xff0c;PostgreSQL&#xff08;以及其他一些SQL数据库系统&#xff09;引入了公共表表达…

变频器基础原理

文章目录 0. 基本知识1.三相的电压之和为02.正弦交流相量的相量表示法(相量只是表示正弦量&#xff0c;而不等于正弦量 &#xff1b;只有正弦量才能用相量表示)引入相量表示法目的:一种正弦量的产生方式:正弦量的相量表示&#xff0c;使用欧拉公式表示复数 3.用复数表示正弦量&…