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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HappyRocking/article/details/83785478

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

如果目标值不存在,则返回 [-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))

猜你喜欢

转载自blog.csdn.net/HappyRocking/article/details/83785478