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

132 阅读1分钟

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

难度 中等

给定一个按照升序排列的整数数组 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

题解

这道题应该是二分查找的一个增强版,简单的二分查找是在一个升序无重复的数组中查找,这个增强版是在升序重复的数组中查找。根据题目意思,需要找到target的第一个位置和最后一个位置。如果我们直接用二分查找,可能出现三种结果,在第一个位置,在第一个位置和最后一个位置的中间,在最后一个位置。但是这道题目不需要中间位置,所以要对我们的二分查找进行修改。

问题是如何进行修改,要定下一个标准,去掉中间位置,还有第一个位置或者最后一个位置,再去掉其中一种可能,我们就可以解决问题的方案确定了。如果我们去掉最后一个位置,每次使用二分修改版,只会查到第一个位置,我们就完成题目的一半了。那最后的位置又如果确定,我们可以查找大于target的数第一个位置,然后再减一。

  • 修改版二分查找

    • 首先没有了nums[mid] == target,这样就避免了取到中间位置的指针的情况
    • nums[mid] > target || (lower && nums[mid] >= target),这段代码左右条件只要一个成立就成立,

      • nums[mid] > target,这表示中间指针的值大于target,
      • (lower && nums[mid] >= target),这表示当lower为true且nums[mid] >= target都成立时才成立

      我们可以带入代码验证一下

      • 当lower为false时,只要nums[mid] > target整个if都成立,会执行下面的代码

        end = ans - 1; 
        ans = mid;  
        

        实现了指针左移,会直到while结束,此时的target刚刚是大于target的第一个位置

      • 当lower为true时,我们发现,只要nums[mid] >= target,整个if都成立,会执行下面的代码

        end = ans - 1; 
        ans = mid;
        
        • 实现了指针左移,跟上面不同的是,会取到nums[mid]=target这种情况,会直到nums[mid]<target,此时的ans刚好是target第一位位置
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int start = search(nums, target, true);//查找第一个位置
        int end = search(nums, target, false) - 1;//查找大于target的数第一个位置,再减一
        if(start <= end && end < nums.length && nums[start] == target && nums[end] == target){//条件判断
            return new int[]{start, end};
        }
        return new int[]{-1, -1};
    }
​
    public int search(int[] nums, int target, boolean lower){
        int start = 0;//左指针
        int end = nums.length - 1;//右指针
        int ans = nums.length;
        while(end >= start){//右指针大于左指针
            int mid = start + ((end - start)  / 2);//中间指针
            if(nums[mid] > target || (lower && nums[mid] >= target)){//见上面文字分解
                end = ans - 1;
                ans = mid;
            }else{
                start = mid + 1;
            }
        }
        return ans;
    }
}