原题
二分与排序中等1 种解法

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

在非递减数组中返回 target 的起止下标,不存在时返回 [-1, -1]。

#数组#二分查找

原题

给你一个按照非递减顺序排列的整数数组 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

查看原题

解题主线

  1. 分别查找第一个不小于 target 和第一个大于 target 的位置,区间即 [lower, upper - 1]。

解法 1:两次下界二分

用统一 lowerBound 分别定位 target 与 target + 1 的边界;为避免 target + 1 溢出,第二次按严格大于查找。

  • 时间复杂度: O(log n)

  • 空间复杂度: O(1)

JAVA
final class Solution {
    public int[] searchRange(int[] nums, int target) {
        int first = lowerBound(nums, target);
        // 下界可能落在数组末端,还需确认该位置确实等于 target
        if (first == nums.length || nums[first] != target) return new int[] {-1, -1};
        int last = upperBound(nums, target) - 1;
        return new int[] {first, last};
    }

    private int lowerBound(int[] nums, int target) {
        // 在左闭右开区间中维护:答案始终位于 [left, right]
        int left = 0, right = nums.length;
        while (left < right) {
            int middle = left + (right - left) / 2;
            // 小于 target 的位置不可能是下界,连同 middle 一起排除
            if (nums[middle] < target) left = middle + 1;
            else right = middle;
        }
        return left;
    }

    private int upperBound(int[] nums, int target) {
        int left = 0, right = nums.length;
        while (left < right) {
            int middle = left + (right - left) / 2;
            // upperBound 寻找首个严格大于 target 的位置
            if (nums[middle] <= target) left = middle + 1;
            else right = middle;
        }
        return left;
    }
}

边界与易错点

  • lower 可能等于数组长度,读取 nums[lower] 前必须检查。
  • 找到一次 target 后不能立即结束,边界仍可能在同侧更远处。

整理来源

由旧仓库源码复核、去重并整理;展示代码已按 Java 21 语义修正明显问题。

  • medium/Q034_search_first_last.java