假设一个数组为【5,7,7,8,8,10】,我们要返回第一个>=8的位置,暴力做法就是遍历每个数,询问是否>=8
那么高效做法就是二分查找,L,R分别指向询问的左右边界,每次询问中间的数
双闭区间left=mid+1 right=mid-1,return left
左闭右开left=mid+1 right=mid 这时候right初始值是len(nums),区间不为空:left<right
左开右闭left=mid right=mid+1 left初始化成-1
双开区间 left=mid right=mid return right(常用)
规律
>=x 就是>=x的情况
>x 就是>=(x+1)
<x 就是大于等于x的那个数的左边那个数 (>=x)-1
<=x 就是>=x左边的那个数 (>x)-1
分析:开始和结束位置就是>=x和<=x
# lower_bound 返回最小的满足 nums[i] >= target 的 i
# 如果数组为空,或者所有数都 < target,则返回 len(nums)
# 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]
# 闭区间写法
def lower_bound(nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1 # 闭区间 [left, right]
while left <= right: # 区间不为空
# 循环不变量:
# nums[left-1] < target
# nums[right+1] >= target
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1 # 范围缩小到 [mid+1, right]
else:
right = mid - 1 # 范围缩小到 [left, mid-1]
return left
# 左闭右开区间写法
def lower_bound2(nums: List[int], target: int) -> int:
left = 0
right = len(nums) # 左闭右开区间 [left, right)
while left < right: # 区间不为空
# 循环不变量:
# nums[left-1] < target
# nums[right] >= target
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1 # 范围缩小到 [mid+1, right)
else:
right = mid # 范围缩小到 [left, mid)
return left # 返回 left 还是 right 都行,因为循环结束后 left == right
# 开区间写法
def lower_bound3(nums: List[int], target: int) -> int:
left, right = -1, len(nums) # 开区间 (left, right)
while left + 1 < right: # 区间不为空
mid = (left + right) // 2
# 循环不变量:
# nums[left] < target
# nums[right] >= target
if nums[mid] < target:
left = mid # 范围缩小到 (mid, right)
else:
right = mid # 范围缩小到 (left, mid)
return right
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
start = lower_bound(nums, target) # 选择其中一种写法即可
if start == len(nums) or nums[start] != target:
return [-1, -1] # nums 中没有 target
# 如果 start 存在,那么 end 必定存在
end = lower_bound(nums, target + 1) - 1
return [start, end]