34 - 在排序数组中查找元素的第一个和最后一个位置 - python

66 阅读2分钟

给定一个按照升序排列的整数数组 nums,和一个目标值 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]

依据于之前所做的题目,如果忽略对于时间复杂度的控制,采用暴力的方法是可以吗解这道题的。首先通过list.index(target)找到第一个等于target的元素的索引,然后判断它后面的元素是不是等于target,直到遇到第一个不等于target的元素,然后将其前一个索引作为终止位置,最后返回索引区间即可。但是这样的方法时间复杂度为 O ( n ) O(n) O(n),不满足要求。

为了达到 O ( log ⁡ n ) O(\log n) O(logn)的时间复杂度要求,一种转换的基本思路就是使用二分查找。另外需要注意的地方在于,数组中的target此时可能有多个重复的值,但给定的数组本身就是升序排列的,所以相同的taget必定是挨在一起的。所以在二分查找的过程中需要如下的判断:

  • 如果nums[mid] == target,则需要向前和向后继续走,直到分别遇到第一个不等于target的元素,此时两个方向的终止位置就是所求区间的左右索引,走的时候同样注意边界
  • 如果nums[mid] < target,说明target位于右端,则更新指针r
  • 如果nums[mid] > target,说明target位于左端,则更新指针l

AC code

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if nums == []: return [-1, -1]
        if len(nums) == 1 and nums[0] == target:
            return [0,0]
        elif len(nums) == 1 and nums[0] != target:
            return [-1, -1] 

        # 题目要求复杂度为O(logn),所以不能使用线性查找
        # 二分查找可用
        l = 0
        r = len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] == target:
                t = mid
                while mid + 1 <= r and nums[mid + 1] == target:
                    mid = mid + 1
                e = mid
                while t - 1 >= 0 and nums[t - 1] == target :
                    t = t - 1
                s = t
                return [s,e]
                
            elif nums[mid] < target:
                l = mid + 1
            else:
                r = mid - 1

        return [-1, -1]