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

leetcode

 

解题思路

该问题需要在有序数组中高效查找目标值的起始和结束位置,核心思路是​​两次二分查找​​,分别定位左边界和右边界。结合搜索结果中关于二分查找的多种实现思路,这里采用​​分界点收缩法​​确保时间复杂度为 O(log n)。


关键步骤

  1. ​​左边界查找​​:
    • 标准二分查找基础上,当 nums[mid] == target 时,仍向左侧收缩右边界,最终左指针位置即为左边界。
  2. ​​右边界查找​​:
    • 类似左边界查找,但当 nums[mid] == target 时,向右侧收缩左边界,最终右指针位置即为右边界。
  3. ​​边界条件处理​​:
    • 检查最终结果是否越界或值不匹配。
  4. ​​整合结果​​:
    • 若左边界存在且值合法,则组合左右边界;否则返回 [-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
}

代码解析

  1. ​​左边界查找逻辑​​:
    • 当 nums[mid] >= target 时,右边界收缩(right = mid - 1),最终 left 指向第一个等于 target 的位置。
    • 循环结束后需检查 left 是否越界或值不匹配。
  2. ​​右边界查找逻辑​​:
    • 当 nums[mid] <= target 时,左边界扩张(left = mid + 1),最终 right 指向最后一个等于 target 的位置。
    • 检查 right 是否越界或值不匹配。
  3. ​​时间复杂度​​:
    • 两次二分查找,时间复杂度为 O(2 log n) → ​​O(log n)​​。
  4. ​​空间复杂度​​:
    • 仅使用常量空间,​​O(1)​​。

运行示例

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]
  2. ​​目标值不存在​​:在左/右边界查找中返回 -1
  3. ​​目标值仅出现一次​​:左/右边界相同(如 nums = [1,3,5], target = 3 → 输出 [1,1])。
  4. ​​全数组均为目标值​​:左边界 0,右边界 len(nums)-1。
posted @ 2025-04-27 12:13  云隙之间  阅读(47)  评论(0)    收藏  举报