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

807 阅读1分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。

题目:给定一个升序数组和一个目标值,要求找出该目标值在数组的开始和结束位置,如果目标值不存在则返回[-1, -1]。要求时间复杂度为O(logN)O(logN)

解题思路

有序数组且时间复杂度为O(logN)O(logN),则首先想到二分法,暂且不看时间复杂度。因为数组有序,我们可以将数组分别从前往后和从后往前进行扫描,如果左边数组的元素等于目标值则记录,这样能确保元素是从前往后数第一个,右边同理可保证元素从后往前数是第一个,这样也能得到答案,代码如下:

public static int[] searchRange(int[] nums, int target) {
        int left=-1, right=-1;
        int l=0, r=nums.length-1;
        while(l<=r){
            if(nums[l]==target){
                left = l;
            }else {
                l++;
            }

            if(nums[r]==target){
                right = r;
            }else {
                r--;
            }

            if(left!=-1&&right!=-1){
                break;
            }
        }
        return new int[]{left, right};
    }

实际运行时间1ms,上述代码时间复杂度为O(N)O(N), 空间复杂度为O(1)O(1)。思想类似双指针,那么如何用二分法在此处呢?直接使用二分可能会遇到nums[mid]==targetnums[mid-1]=nums[mid+1]=target的情况,这种情况会比较复杂,因此此处可以对问题进行转化。

转化思路为:

  1. 题目所需是获取指定元素的开始和结束索引,那么可以转化成寻找一个数组中大于某个元素的第一个索引。
  2. 即寻找大于target-1的第一个索引,若存在则是target的第一个索引。
  3. 寻找大于target的第一个索引,若存在则index-1即为target的最后一个索引。

此处的index的初始化必须为数组的长度,因为如果数组中不存在大于target的元素,则代表当前target必然是数组的最后一个元素,而之后需要将得到的index-1,则此时应该返回数组的长度。代码如下:

public static int[] searchRange2(int[] nums, int target) {
        int left = binarySearch(nums, target-1);
        int right = binarySearch(nums, target) - 1;
        if(left<=right){
            return new int[]{left, right};
        }
        return new int[]{-1, -1};

    }

    public static int binarySearch(int[] nums, int target){
        int l=0, n=nums.length, r=n-1, index=n; //此处index初始化必须为数组长度,否则报错
        while (l<=r){
            int mid = (l + r) / 2;
            if(nums[mid]>target){
                r=mid-1;
                index=mid;
            }else {
                l=mid+1;
            }
        }
        return index;
    }

上述算法因为是二分查找,其算法的时间复杂度为O(logN)O(logN),算法中用了常数项的变量,空间复杂度为O(1)O(1)