当前:LC34 · 查找首尾位置 · 两道闸门 lowerBound · 首次出现于 Day 7 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC34 · First and Last Position · 二分

查找元素的首尾位置:两次二分

首位置 = 第一个 ≥ target;尾位置 = 第一个 > target 减一。

用两次二分:第一次找 lower_bound(第一个 ≥ target 的下标),第二次找 upper_bound(第一个 > target 的下标)。若 lower_bound 处不是 target 或超出数组,说明不存在,返回 [−1,−1];否则返回 [lower, upper−1]。O(log n)。

时间 O(log n)空间 O(1)结论先行 · 全文约 6 节
导读

给定升序数组 nums 和目标值 target,返回 target 在数组中的开始和结束位置;不存在返回 [−1,−1]。

示例:nums = [5,7,7,8,8,10],target = 8 → [3,4]。

一次二分只能找到一个位置——首和尾需要两种不同的二分。

首尾位置 = 两个边界

target 可能出现多次。它的「首位置」是第一个等于 target 的位置,即 lower_bound。

它的「尾位置」是最后一个等于 target 的位置,等于 upper_bound(第一个 > target 的位置)减一。

所以问题变成两个独立的二分:找 lower_bound 和 upper_bound。

边界思维区间 [lower, upper−1] 正好框住所有等于 target 的元素——用开区间/闭区间的边界定界。

第一次二分:找 lower_bound

lower_bound = 第一个满足 nums[i] ≥ target 的下标。

规则:nums[mid] < target → left = mid+1(排除);否则 right = mid−1。

nums = [5,7,7,8,8,10],target = 8:

mid=2:7 < 8 → left=3。

mid=4:8 ≥ 8 → right=3。

mid=3:8 ≥ 8 → right=2。

left=3 > right=2,lower_bound = 3。

lower_bound:第一个 ≥ target 的位置
5
0
7
1
7
2
8
3
8
4
10
5
[left=3, right=5]mid=4nums[4]=8 ≥ 8 → right=3

第二次二分:找 upper_bound

upper_bound = 第一个满足 nums[i] > target 的下标。

规则:nums[mid] ≤ target → left = mid+1;否则 right = mid−1。

nums = [5,7,7,8,8,10],target = 8:

mid=2:7 ≤ 8 → left=3。

mid=4:8 ≤ 8 → left=5。

mid=5:10 > 8 → right=4。

left=5 > right=4,upper_bound = 5。

答案 = [3, 5−1] = [3,4]。

upper_bound:第一个 > target 的位置
5
0
7
1
7
2
8
3
8
4
10
5
[left=3, right=5]mid=4nums[4]=8 ≤ 8 → left=5

不存在的情况

如果 lower_bound 越界,或者 nums[lower_bound] ≠ target,说明 target 不存在,返回 [−1,−1]。

举例:target = 6,lower_bound 会停在第一个 ≥ 6 的位置(下标 1 的值 7),nums[1] = 7 ≠ 6 → 不存在。

这个判空条件很重要:两次二分本身不会告诉你「不存在」。

Go:lower_bound + upper_bound

solution.goGo
func lowerBound(nums []int, t int) int {
l, r := 0, len(nums)-1
for l <= r {
m := l + (r-l)/2
if nums[m] < t { l = m + 1 } else { r = m - 1 }
}
return l
}
func searchRange(nums []int, target int) []int {
lo := lowerBound(nums, target)
if lo == len(nums) || nums[lo] != target { return []int{-1, -1} }
return []int{lo, lowerBound(nums, target+1) - 1}
}

1lower_bound 找第一个 ≥ target。

2小于 target 排除左半边。

3upper_bound 复用同一函数:找第一个 ≥ target+1,即第一个 > target。

4判空:越界或值不等 → 不存在。

总结

lower_bound 与 upper_bound 两次二分,框出 target 的全部位置。

  • 首位置 = 第一个 ≥ target,尾位置 = 第一个 > target 减一。
  • 两种边界只有一处判断不同:< 还是 ≤。
  • 判空靠「值不等于 target 或越界」,二分本身不报不存在。
同族题目
LC35搜索插入位置LC704二分查找LC278第一个错误的版本