Leetcode演算法——34、有序陣列查詢元素的首尾位置
阿新 • • 發佈:2018-11-08
給定一個升序整數陣列,找到一個目標值的起始和結束位置。
如果目標值不存在,則返回 [-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))