全栈开发知识库

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

题目

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。

请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

  • 输入:nums = [5,7,7,8,8,10], target = 8
  • 输出:[3,4]

示例 2:

  • 输入:nums = [5,7,7,8,8,10], target = 6
  • 输出:[-1,-1] 示例 3:

  • 输入:nums = [], target = 0

  • 输出:[-1,-1]

思路

数组是排序好的(升序),这就意味着相同的数字都挤在一起。

不用线性扫描,而是用两次二分查找:

  • 第一次二分: 找第一个等于target的位置

    • 找到target时,不着急返回,继续往左边找,看看还有没有更早的
  • 第二次二分: 找最后一个等于target的位置 找到target时,不着急返回,继续往右找,看看还有没有更晚的

fun searchRange(nums: IntArray, target: Int): IntArray {
    // 先找第一个位置
    val first = findFirst(nums, target)
    // 再找最后一个位置
    val last = findLast(nums, target)

    return intArrayOf(first, last)
}

// 找第一个等于target的位置
private fun findFirst(nums: IntArray, target: Int): Int {
    var left = 0
    var right = nums.size - 1
    var result = -1

    while (left <= right) {
        val mid = left + (right - left) / 2

        when {
            nums[mid] == target -> {
                result = mid      // 记录当前位置
                right = mid - 1   // 继续往左边找,看还有没有更早的
            }
            nums[mid] < target -> left = mid + 1
            else -> right = mid - 1
        }
    }

    return result
}

// 找最后一个等于target的位置
private fun findLast(nums: IntArray, target: Int): Int {
    var left = 0
    var right = nums.size - 1
    var result = -1

    while (left <= right) {
        val mid = left + (right - left) / 2

        when {
            nums[mid] == target -> {
                result = mid      // 记录当前位置
                left = mid + 1    // 继续往右边找,看还有没有更晚的
            }
            nums[mid] < target -> left = mid + 1
            else -> right = mid - 1
        }
    }

    return result
}

Q:能不能只用一次二分就搞定?​

A:可以,但比较复杂。思路是先二分找到一个target的位置,然后向两边扩展找到边界。但在最坏情况下(全是target)会退化成O(n)。所以标准解法还是两次二分。

Q:如果数组为空怎么办?​

A:代码已经处理好了,findFirst和findLast都会返回-1。

Q:这和普通的二分查找有什么区别?​

A:普通二分找到target就直接返回了。这里找到后不着急走,还要继续找边界,这就是"二分查找边界"的技巧。

复杂度

  • 时间复杂度: O(log n) — 两次二分,每次O(log n)
  • 空间复杂度: O(1) — 只用了几个变量


  • 邮箱 :charon.chui@gmail.com
  • Good Luck!
本页由 Markdown 自动渲染 · 共 628 篇笔记