Leetcode算法——34、有序数组查询元素的首尾位置

76 篇文章 1 订阅

给定一个升序整数数组,找到一个目标值的起始和结束位置。

如果目标值不存在,则返回 [-1,-1]。

示例:
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]

思路

在一个有序数组中查询某个目标值的位置,可以使用经典的二分法。

但是本题不仅需要查询位置,还需要查询最开始出现的位置和最晚出现的位置。

因此需要在二分法的基础上,进行修改,使得二分法可以返回一个数组,分别存放目标值的首尾位置。

我们使用递归法进行二分,每次递归进行:
1、寻找到当前数组的中位数
2、比较中位数与目标值的大小

  • 若目标值=中位数,则说明最终结果的首尾位置会分别出现在左半部分和右半部分(包括中位数本身),因此需要分别递归计算出左半部分和右半部分的首尾位置,然后取出最小的首位置和最大的尾位置,作为最终结果。
  • 若目标值<中位数,则说明最终结果的首尾位置只可能出现在左半部分,继续递归左半部分即可。
  • 若目标值>中位数,则说明最终结果的首尾位置只可能出现在右半部分,继续递归右半部分即可。

3、递归结束条件

  • 若当前数组小于等于2,则直接对所有元素扫描一遍,返回目标值的首尾位置。

小技巧:
1、若当前数组的首尾元素相等,则由于是有序数组,因此中间所有元素也都相等。这样不必继续递归,直接根据元素值是否等于目标值即可返回结果。
2、如果没有找到首尾位置,则建议首位置返回原数组的长度,尾位置返回-1。这是因为首位置的最大值是数组长度-1,尾位置最小值是0,但凡其他递归的结果中寻找到了目标值,那么两对结果进行比较时,只需要选择较小的首位置和较大的尾位置作为最终结果即可。

python实现

def searchRange(nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: List[int]
    二分法。
    在查找到target之后,继续使用二分法,查找起始和结束位置。
    """
    
    def binary_search(nums, l, u, target, max_len):
        '''
        二分法查找nums的l~u之间等于target的元素,返回起始和结束位置。
        如果不存在,则返回 (max_len, -1)
        '''
        # 递归结束条件1
        if u - l <= 1: # 数组长度小于等于2
            # 初始化首尾位置
            start = max_len
            end = -1
            # 依次与目标值进行比较
            for i in range(l, u+1):
                if nums[i] == target:
                    start = min(start, i)
                    end = max(end, i)
            return (start, end)
        
        # 递归结束条件2
        if nums[l] == nums[u]: # 数组首尾元素相等,则中间元素也都相等
            if nums[l] == target:
                return (l, u)
            else:
                return (max_len, -1)
        
        # 开始二分法
        mid = int((l+u)/2)
        if nums[mid] == target: # 中位数等于目标值
            # 寻找左半部分的首尾位置
            start1, end1 = binary_search(nums, l, mid, target, max_len)
            # 寻找右半部分的首尾位置
            start2, end2 = binary_search(nums, mid, u, target, max_len)
            # 两个首尾位置比较大小
            start = min(start1, start2)
            end = max(end1, end2)
            return (start, end)
        elif nums[mid] > target: # 目标值小于中位数
            # 寻找左半部分的首尾位置
            return binary_search(nums, l, mid, target, max_len)
        else:
            # 否则,寻找右半部分的首尾位置
            return binary_search(nums, mid, u, target, max_len)
    
    if not nums or target < nums[0] or target > nums[-1]:
        return [-1, -1]
    max_len = len(nums)
    start, end = binary_search(nums, 0, max_len-1, target, max_len)
    if start == max_len:
        start = -1
    return [start, end]

if '__main__' == __name__:
    nums = [5,6,7,8,8,10]
    target = 10
    print(searchRange(nums, target))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值