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

题目地址
最近忙活实验,实在没空刷题,这个题对我来说难度还蛮大的,尤其是理解那个找左边界和找右边界的条件,后来我按照自己的理解写了出来(感觉给的答案解释起来有点反认识规律),所以我从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};
    }
};

结果也是顺利通过:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三轴加速度计LIS2DUX12开发(2)----静态校准

三轴加速度计LIS2DUX12开发.2----静态校准 概述硬件准备视频教学样品申请源码下载六位置法的标定方案旋转加速度计以找到极值计算偏移和灵敏度应用校准参数注意事项串口中断变量定义主程序流程串口发送定义演示 概述 最近在弄ST和瑞萨RA的课程&#xff0c;需要样片的可以加群申…

木马——文件上传

目录 1、WebShell 2.一句话木马 靶场训练 3.蚁剑 虚拟终端 文件管理 ​编辑 数据操作 4.404.php 5.文件上传漏洞 客户端JS检测 右键查看元素&#xff0c;删除检测代码 BP拦截JPG修改为php 服务端检测 1.MIME类型检测 2.文件幻数检测 3.后缀名检测 1、WebShell W…

【Hadoop】-HDFS的Shell操作[3]

目录 前言 一、HDFS集群启停命令 1.一键启停脚本可用 2.独立进程启停可用 二、文件系统操作命令 1、创建文件夹 2、查看指定目录下内容 3、上传文件到HDFS指定目录下 4、查看HDFS文件内容 5、下载HDFS文件 6、拷贝HDFS文件 7、追加数据到HDFS文件中 8、HDFS数据移…

【信号处理】基于CNN的心电(ECG)信号分类典型方法实现(tensorflow)

关于 本实验使用1维卷积神经网络实现心电信号的5分类。由于数据类别不均衡&#xff0c;这里使用典型的上采样方法&#xff0c;实现数据类别的均衡化处理。 工具 方法实现 数据加载 Read the CSV file datasets: NORMAL_LABEL0 , ABNORMAL_LABEL1,2,3,4,5 ptbdb_abnormalpd.…

使用JavaScript收集和发送用户设备信息,后端使用php将数据保存在本地json,便于后期分析数据

js代码部分 <script> // 之前提供的JavaScript代码 fetch(https://api.ipify.org?formatjson).then(response > response.json()).then(data > {const deviceInfo {userAgent: navigator.userAgent,platform: navigator.platform,language: navigator.language,…

[Spring Cloud] (4)搭建Vue2与网关、微服务通信并配置跨域

文章目录 前言gatway网关跨域配置取消微服务跨域配置 创建vue2项目准备一个原始vue2项目安装vue-router创建路由vue.config.js配置修改App.vue修改 添加接口访问安装axios创建request.js创建index.js创建InfoApi.js main.jssecurityUtils.js 前端登录界面登录消息提示框 最终效…

微信小程序vue.js+uniapp服装商城销售管理系统nodejs-java

本技术是java平台的开源应用框架&#xff0c;其目的是简化Sping的初始搭建和开发过程。默认配置了很多框架的使用方式&#xff0c;自动加载Jar包&#xff0c;为了让用户尽可能快的跑起来spring应用程序。 SpinrgBoot的主要优点有&#xff1a; 1、为所有spring开发提供了一个更快…

贝叶斯分类 python

贝叶斯分类 python 贝叶斯分类器是一种基于贝叶斯定理的分类方法&#xff0c;常用于文本分类、垃圾邮件过滤等领域。 在Python中&#xff0c;我们可以使用scikit-learn库来实现贝叶斯分类器。 下面是一个使用Gaussian Naive Bayes(高斯朴素贝叶斯)分类器的简单示例&#xff1…

大数据Hive中的UDF:自定义数据处理的利器(上)

文章目录 1. 前言2. UDF与宏及静态表的对比3. 深入理解UDF4. 实现自定义UDF 1. 前言 在大数据技术栈中&#xff0c;Apache Hive 扮演着数据仓库的关键角色&#xff0c;它提供了丰富的数据操作功能&#xff0c;并通过类似于 SQL 的 HiveQL 语言简化了对 Hadoop 数据的处理。然而…

汇编语言(详解)

汇编语言安装指南 第一步&#xff1a;在github上下载汇编语言的安装包 网址&#xff1a;GitHub - HaiPenglai/bilibili_assembly: B站-汇编语言-pdf、代码、环境等资料B站-汇编语言-pdf、代码、环境等资料. Contribute to HaiPenglai/bilibili_assembly development by creat…

STM32 | USART实战案例

STM32 | 通用同步/异步串行接收/发送器USART带蓝牙(第六天)随着扩展的内容越来越多,很多小伙伴已经忘记了之前的学习内容,然后后面这些都很难理解。STM32合集已在专栏创建,方面大家学习。1、通过电脑串口助手发送数据,控制开发板LED灯 从题目中可以挖掘出,本次使用led、延…

【JVM常见问题总结】

文章目录 jvm介绍jvm内存模型jvm内存分配参数jvm堆中存储对象&#xff1a;对象在堆中创建分配内存过程 jvm 堆垃圾收集器垃圾回收算法标记阶段引用计数算法可达性分析算法 清除阶段标记清除算法复制算法标记压缩算法 实际jvm参数实战jvm调优jvm常用命令常用工具 jvm介绍 Java虚…

C++设计模式:适配器模式(十四)

1、定义与动机 定义&#xff1a;将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作。 动机&#xff1a; 在软件系统中&#xff0c;由于应用环境的变化&#xff0c;常常需要将“一些现存的对象”放在新的环境…

【Hadoop】- YARN概述[6]

目录 一、YARN & Reduce 二、分布式资源调度 - YARN 1、资源调度 2、YARN的资源调度 总结 一、YARN & Reduce MapReduce是基于YARN运行的&#xff0c;即没有YARN “无法” 运行MapReduce程序。 二、分布式资源调度 - YARN YARN&#xff08;Yet Another Resou…

注意力机制中多层的作用

1.多层的作用 在注意力机制中&#xff0c;多层的作用通常指的是将注意力机制堆叠在多个层上&#xff0c;这在深度学习模型中被称为“深度”或“多层”注意力网络。这种多层结构的作用和实现过程如下&#xff1a; 1. **逐层抽象**&#xff1a;每一层都可以捕捉到输入数据的不同…

Oracle之SQL plus的一些经验心得

每次登入SQL plus后,不知道时哪个用户登入,非常不方便,只能使用show user查看。 以下时可以通过一些设置实现上述的效果,知道时哪个用户登入,和实现输出效果等 1)SQL plus使用细则 SQL plus登录时,我们可以设置一些通用的设置,在每次登入SQL plus的时候生效。 [root@c…

Eclipse+Java+Swing实现学生信息管理系统-TXT存储信息

一、系统介绍 1.开发环境 操作系统&#xff1a;Win10 开发工具 &#xff1a;Eclipse2021 JDK版本&#xff1a;jdk1.8 存储方式&#xff1a;Txt文件存储 2.技术选型 JavaSwingTxt 3.功能模块 4.工程结构 5.系统功能 1.系统登录 管理员可以登录系统。 2.教师-查看学生…

rmallox勒索病毒威胁网络安全:如何避免数据被锁定

尊敬的读者&#xff1a; 随着信息技术的飞速发展&#xff0c;网络空间的安全问题日益凸显。近年来&#xff0c;一种名为.rmallox的勒索病毒频繁出没&#xff0c;给广大计算机用户带来了严重的困扰。本文将对该病毒进行深入剖析&#xff0c;并探讨相应的应对策略。在面对被勒索…

VulnHub靶机 DC-7 打靶 渗透详细流程

VulnHub靶机 DC-7 实战打靶 详细渗透测试流程 目录 VulnHub靶机 DC-7 实战打靶 详细渗透测试流程一、将靶机配置文件导入虚拟机当中二、渗透测试流程主机发现端口扫描目录爆破web渗透白盒测试ssh远程连接 提权修改后台密码GETSHELL反弹shell 一、将靶机配置文件导入虚拟机当中 …

深度神经网络(DNN)

通过5个条件判定一件事情是否会发生&#xff0c;5个条件对这件事情是否发生的影响力不同&#xff0c;计算每个条件对这件事情发生的影响力多大&#xff0c;写一个深度神经网络&#xff08;DNN&#xff09;模型程序,最后打印5个条件分别的影响力。 示例 在深度神经网络&#xf…