leetcode
解题思路
该问题需要在有序数组中高效查找目标值的起始和结束位置,核心思路是两次二分查找,分别定位左边界和右边界。结合搜索结果中关于二分查找的多种实现思路,这里采用分界点收缩法确保时间复杂度为 O(log n)。
关键步骤
- 左边界查找:
- 标准二分查找基础上,当
nums[mid] == target 时,仍向左侧收缩右边界,最终左指针位置即为左边界。
- 右边界查找:
- 类似左边界查找,但当
nums[mid] == target 时,向右侧收缩左边界,最终右指针位置即为右边界。
- 边界条件处理:
- 整合结果:
- 若左边界存在且值合法,则组合左右边界;否则返回
[-1, -1]。
代码实现
func searchRange(nums []int, target int) []int {
if len(nums) == 0 {
return []int{-1, -1}
}
// 查找左边界
left := findLeft(nums, target)
if left == -1 {
return []int{-1, -1}
}
// 查找右边界
right := findRight(nums, target)
return []int{left, right}
}
// 查找左边界(第一个等于target的位置)
func findLeft(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2 // 避免溢出
if nums[mid] >= target { // 关键:等于时继续向左收缩
right = mid - 1
} else {
left = mid + 1
}
}
// 检查是否越界或值不匹配
if left >= len(nums) || nums[left] != target {
return -1
}
return left
}
// 查找右边界(最后一个等于target的位置)
func findRight(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] <= target { // 关键:等于时继续向右收缩
left = mid + 1
} else {
right = mid - 1
}
}
// 检查是否越界或值不匹配
if right < 0 || nums[right] != target {
return -1
}
return right
}
代码解析
- 左边界查找逻辑:
- 当
nums[mid] >= target 时,右边界收缩(right = mid - 1),最终 left 指向第一个等于 target 的位置。
- 循环结束后需检查
left 是否越界或值不匹配。
- 右边界查找逻辑:
- 当
nums[mid] <= target 时,左边界扩张(left = mid + 1),最终 right 指向最后一个等于 target 的位置。
- 检查
right 是否越界或值不匹配。
- 时间复杂度:
- 两次二分查找,时间复杂度为 O(2 log n) → O(log n)。
- 空间复杂度:
运行示例
func main() {
// 示例 1
nums1 := []int{5,7,7,8,8,10}
fmt.Println(searchRange(nums1, 8)) // 输出: [3 4]
// 示例 2
nums2 := []int{5,7,7,8,8,10}
fmt.Println(searchRange(nums2, 6)) // 输出: [-1 -1]
// 示例 3
nums3 := []int{}
fmt.Println(searchRange(nums3, 0)) // 输出: [-1 -1]
}
边界条件处理
- 空数组:直接返回
[-1, -1]。
- 目标值不存在:在左/右边界查找中返回
-1。
- 目标值仅出现一次:左/右边界相同(如
nums = [1,3,5], target = 3 → 输出 [1,1])。
- 全数组均为目标值:左边界 0,右边界
len(nums)-1。