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

96 阅读3分钟

题目

🔗题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:你可以设计并实现时间复杂度为 O(logn)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]

思路

寻找 target 在数组中的左右边界,有如下三种情况:

  1. target 在数组的左边或右边。例如 nums = [3, 4, 5],target = 2。此时应该返回 [-1, -1]。
  2. target 在数组范围内,但数组中不存在 target。例如 nums = [3, 6, 7],target = 5。此时应该返回 [-1, -1]。
  3. target 在数组范围内,且数组中存在 target。例如 nums = [3, 6, 7],target = 6。此时应该返回[1, 1]。

代码

寻找右边界

// 寻找右边界,就是让 left 往右走
function getRightBorder(nums: number[], target: number): number {
    const lens = nums.length;
    
    // 定义 target 在左闭右闭的区间内 [left, right]
    let left = 0;
    let right = lens - 1;
    
    let rightBorder = -2;
    
    while (left <= right) {
        let mid = Math.floor(left + (right - left) / 2);
        
        if (target < nums[mid]) {
            // target 在左边
            // 如果 target 在数组的左边,只会更新 right,不会更新 left 和 rightBorder。这个函数返回的时候 rightBorder 仍为 -2。
            right = mid - 1;
        } else if (target > nums[mid]) {
            // 为什么在这里也要更新 rightBorder 
            // 因为数组中可能不存在与 target 相等的值
            left = mid + 1;
            rightBorder = left;
        } else {
            // 当 nums[mid] = target 的时候,让 left 继续走,这样可以得到 target 的右边界
            left = mid + 1;
            rightBorder = left;
        }
    }
    
    return rightBorder;
}

寻找左边界

// 寻找左边界,就是让 left 往左走
function getLeftBorder(nums: number[], target: number): number {
    const lens = nums.length;
    
    // 定义 target 在左闭右闭的区间内 [left, right]
    let left = 0;
    let right = lens - 1;
    
    let leftBorder = -2;
    
    while (left <= right) {
        let mid = Math.floor(left + (right - left) / 2);
        
        if (target < nums[mid]) {
            // 为什么在这里也要更新 leftBorder 
            // 因为数组中可能不存在与 target 相等的值
            right = mid - 1;
            leftBorder = right;
        } else if (target > nums[mid]) {
            left = mid + 1;
        } else {
            // 当 nums[mid] = target 的时候,让 right 继续走,这样可以得到 target 的左边界
            right = mid - 1;
            leftBorder = right;
        }
    }
    
    return leftBorder;
}

完整代码(需要加上寻找左右边界的代码)

function searchRange(nums: number[], target: number): number[] {
    const leftBorder = getLeftBorder(nums, target);
    const rightBorder = getRightBorder(nums, target);
    // 情况一
    // 如果左边界或者右边界未被赋值(-2),表示目标值不在数组中,在数组的左边或者右边
    // 返回 [-1, -1]
    if (-2 === leftBorder || -2 === rightBorder) return [-1, -1];
    // 情况三
    // 如果左右边界之间存在超过一个元素,则返回目标值的起始位置和结束位置
    if (rightBorder - leftBorder > 1) return [leftBorder + 1, rightBorder - 1];
    // 情况二
    // 如果左右边界相邻或者相等,表示 target 的大小在数组范围内,但数组中没有与 target 相等的值
    // 返回 [-1, -1]
    return [-1, -1];
};