题目背景
在排序数组中查找元素的第一个和最后一个位置,是二分查找的经典变体。给定一个非递减顺序排列的整数数组 nums 和一个目标值 target,要求找出该目标值在数组中的开始位置和结束位置。如果不存在,返回 [-1, -1]。
核心约束是算法的时间复杂度必须为 O(log n),这意味着不能遍历数组,必须利用二分查找的特性。
核心思路:二段性
二分查找的本质是利用数据的单调性(二段性)。我们将搜索区间划分为两部分:一部分满足某种性质,另一部分不满足。通过不断缩小区间,最终锁定边界点。
对于本题,我们需要分别找到左边界和右边界。
1. 寻找左边界
目标是找到第一个大于等于 target 的位置。
-
区间划分:
- 左半段:元素值小于
target(不满足条件) - 右半段:元素值大于等于
target(满足条件)
- 左半段:元素值小于
-
指针移动策略:
- 计算中点时,采用向下取整:
mid = left + (right - left) / 2 - 如果
nums[mid] < target,说明中点在左半段,目标在右侧,更新left = mid + 1 - 否则,中点在右半段,可能是答案或答案在左侧,更新
right = mid
- 计算中点时,采用向下取整:
-
循环终止:
- 当
left == right时停止。此时left指向的就是左边界候选位置。
- 当
2. 寻找右边界
目标是找到最后一个小于等于 target 的位置。
-
区间划分:
- 左半段:元素值小于等于
target(满足条件) - 右半段:元素值大于
target(不满足条件)
- 左半段:元素值小于等于
-
指针移动策略:
- 计算中点时,采用向上取整:
mid = left + (right - left + 1) / 2 - 为什么加 1?这是为了防止死循环。当
left和right相邻且mid向下取整会等于left时,若left需要向右移动,会导致mid不变,陷入死循环。向上取整确保mid偏向右侧。 - 如果
nums[mid] <= target,说明中点在左半段,可能是答案或答案在右侧,更新left = mid - 否则,中点在右半段,更新
right = mid - 1
- 计算中点时,采用向上取整:
-
循环终止:
- 同样当
left == right时停止。
- 同样当
3. 避免死循环的关键
在二分查找中,死循环通常发生在 left 和 right 相邻,且 mid 的计算方式导致指针无法缩小区间时。
- 找左边界时, 会收敛到 ,所以 必须偏左(向下取整),这样 才能有机会移动到 。


