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

305 阅读2分钟

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

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

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

题目要求在一个升序排列的数组中查找一个目标值的开始和结束位置,明显的从升序数组、目标值两个关键字可以想到使用二分查找,但是现在要求的是查找目标值的开始和结束两个位置,其实也还是可以使用二分查找的,只不过就是需要查找两次而已,一次找开始位置,一次找结束位置。

首先二分查找会进行2次,一次为查询目标值target在数组中第一次出现的位置leftIndex(开始位置),一次查询目标值target在数组中最后一次出现的位置rightIndex(结束位置)。对于基本的二分查询是找到等于目标值target的位置时就直接返回,这种情况就不能用于查找边界值了。比如查找左边界时,当找到目标值时不能直接返回,需要继续查询左边区间,缩小区间范围,最后返回边界值就可以了。同理查找右边界时也是一样,查找到目标值时不能直接返回,通过续查询右边界,缩小右区间范围,来找到右边界的。最后获取到的两个边界值可能不一定符号要求(目标值不在数组内时),所以需要额外判断一下。

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        if (nums.length == 0) {
            res[0] = -1;
            res[1] = -1;
            return res;
        }
        // 找左边界
        int leftIndex = 0;
        int rightIndex = nums.length - 1;
        while (leftIndex < rightIndex) {
            int mid = (leftIndex + rightIndex) / 2;
            if (nums[mid] >= target) {
                rightIndex = mid;
            } else {
                leftIndex = mid + 1;
            }
        }
        // 都没找到不用继续了
        if (nums[rightIndex] != target) {
            res[0] = -1;
            res[1] = -1;
            return res;
        }
        res[0] = rightIndex;
        // 找右边界
        leftIndex = 0;
        rightIndex = nums.length - 1;
        while (leftIndex < rightIndex) {
            int mid = (leftIndex + rightIndex + 1) / 2;
            if (nums[mid] <= target) {
                leftIndex = mid;
            } else {
                rightIndex = mid - 1;
            }
        }
        res[1] = rightIndex;
        return res;
    }
}