1. 程式人生 > >Leetcode演算法——34、有序陣列查詢元素的首尾位置

Leetcode演算法——34、有序陣列查詢元素的首尾位置

給定一個升序整數陣列,找到一個目標值的起始和結束位置。

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