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

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

题目链接
代码随想录

分析

非递减顺序的意思是升序但是可能存在重复元素,O(logn),可以尝试使用二分法。

思路是用一个二分法找结果范围的左边界,再写一个二分法找结果范围的右边界,重点是如何通过二分法查找结果范围的单个边界,
其实方式很简单,以查找结果范围的右边界为例,还是通过二分法来查找,获取区间的中间元素的时候,如果中间元素大于目标值,就更新右区间,如果中间元素小于等于目标值都更新左区间,同时如果 nums[mid]==target,则更新结果范围的右边界,这样的话,扫描完所有的元素之后,我们得到的结果范围的右边界就是索引最大的。

有的时候我们会想使用 left 或者 right 来存储结果范围的左边界或者右边界的值,这个不建议这样做,因为 left 和 right 是循环变量。我们尽量让变量的作用单一。

这里跟经典的二分查找的区别是,经典的二分法写法是遇到目标值就跳出循环,但是这里是查找右边界,因此 nums[mid]==target 依然更新左区间,继续收缩区间

有的时候会想会不会跳过了最终边界,答案是不会,
例如 5 5 5 8 10 12 14 15,目标是是 5

究其原因,主要因为数列是有序的,而且在每一次循环中,我们都在不断更新区间的左右区间,因此,只要我们对左右区间的定义不变,更新左右区间时的操作不出问题,那么在最终跳出循环的时候,我们就能按照我们设定好的方式,遍历完序列中所有的值,二分法的实际效果就是,即使只挑选几个值(每次区间的 mid 值),也能保证不遗漏。二分查找的本质,就是有序数列的一种快速遍历方式
左边界同理。

常规的二分法有三个变量,查询区间 [start,end] 和 区间中间索引值 mid,但是查找边界的二分法有第四个变量,边界 border。 而且常规的二分法以 mid 为最终值,但是查找边界的时候,border 值才是最终返回的值,二分法只是为了快速遍历。为什么二分法能快速遍历,因为数组有序。

通过这个题,我们对二分法的的使用应该有一个更深层次的理解,二分法本质上是一种从有序数组两侧快速往中间逼近的方法,可以有效加快逼近的速度,而且不会遗漏元素。
而且这个题也让我们明白,只要是有序数组,不管有没有重复元素,都可以用二分法

因为本题使用了两次二分查找,所以算法复杂度应该是 O(2logn),常数系数可以去掉,所以最终的复杂度是 O(logn)

解题

public int[] searchRange(int[] nums, int target) {
    int leftBorder = binarySearchFromLeft(nums,target);
    int rightBorder = binarySearchFromRight(nums,target);
    return new int[]{leftBorder,rightBorder};
}

public int binarySearchFromRight(int[] nums,int target){
    int start=0, end = nums.length-1;
    int rightBorder = -1;
    while(start<=end){
        int mid = start+(end-start)/2;
        if (nums[mid]>target){
            // 从右侧确定右边界
            end = mid-1;
        } else {
            start = mid+1;
            if(nums[mid]==target){
                rightBorder = mid;
            }
        }
    }
    return rightBorder;
}

public int binarySearchFromLeft(int[] nums,int target){
    int start=0, end = nums.length-1;
    int leftBorder = -1;
    while(start<=end){
        int mid = start+(end-start)/2;
        if (nums[mid]<target){
            // 从左侧确定左边界
            start = mid+1;
        } else {
            end = mid-1;
            if(nums[mid]==target){
                leftBorder = mid;
            }
        }
    }
    return leftBorder;
}

相关题

704. 二分查找
35. 搜索插入位置
69. x 的平方根