【二分查找】力扣34:在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 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]

  1. 二分查找
    直观的思路肯定是从前往后遍历一遍。用两个变量记录第一次和最后一次遇见 target 的下标,但这个方法的时间复杂度为 O(n),没有利用到数组升序排列的条件。
    由于数组已经排序,因此整个数组是单调递增的,我们可以利用二分法来加速查找的过程。
    考虑 target 开始和结束位置,其实我们要找的就是数组中「第一个等于 target 的位置」和「第一个大于target 的位置减一」。因此用两个二分查找,一个二分查找查找左边界,另一个查找右边界。

要找 target 在 nums 数组中的左右边界,存在 3 种情况:

  • target 在 nums[0] ~ nums[n-1] 中,nums 中存在 target。例如 nums = [5,7,7,8,8,10],target = 8,返回 [3,4]。
  • target 在 nums[0] ~ nums[n-1] 中,nums 中不存在 target。例如 nums = [5,7,7,8,8,10],target = 6,返回 [-1,-1]。
  • target < nums[0] 或者 target > nums[n-1]。例如 nums = [5,7,7,8,8,10], target = 4,返回 [-1,-1]。
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if len(nums) == 0 or target < nums[0] or target > nums[-1]:
           return [-1, -1] # 首先考虑不存在的情况
        lm = self.leftMargin(nums, target)
        rm = self.rightMargin(nums, target)
        return [lm,rm]

    # 找左边界
    def leftMargin(self, nums: List[int], target: int):
        low, high = 0, len(nums) - 1 # 初始化左右指针
        while low <= high:
            mid = low + (high - low) // 2  # mid向下取整
            if nums[mid] < target:
                low = mid + 1
            else:
                high = mid - 1  # 普通二分查找是,当 nums[mid] == target 时,直接返回 mid,而在本题中,则是要继续向左查找,看是否还有和 target 相等的数组元素
        if nums[low] == target:
            return low
        else:
            return -1

    # 找右边界
    def rightMargin(self, nums: List[int], target: int):
        low, high = 0, len(nums) - 1 # 初始化左右指针
        while low <= high:
            mid = low + (high - low) // 2  # mid向下取整
            if nums[mid] <= target:
                low = mid + 1  # 相等的时候也要一直向右找,看是否还有和 target 相等的数组元素
            else:
                high = mid - 1
        if nums[high] == target:
            return high
        else:
            return -1

时间复杂度:O(logn) ,其中 n 为数组的长度。二分查找的时间复杂度为 O(logn),一共会执行两次,因此总时间复杂度依旧为 O(logn)。
空间复杂度:O(1) 。只需要常数空间存放若干变量。

思路可视化:ACM 选手图解 LeetCode 在排序数组中查找元素的第一个和最后一个位置 | 编程文青李狗蛋 - 在排序数组中查找元素的第一个和最后一个位置

  1. 一行代码
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # index为target在列表中的第一个下标,用count计算target在nums出现的次数,结束值为两者相加减一
        if target in nums:
            return [nums.index(target),nums.index(target) + nums.count(target) - 1]
        else: return [-1,-1]

作者:zhu-ba-ba-r
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/li-yong-indexfan-hui-xia-biao-zhi-1xing-u2k3n/
posted @   Vonos  阅读(57)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示