給定一個按照升序排列的整數數組 nums,和一個目標值 target。找出給定目標值在數組中的開始位置和結束位置。
你的算法時間複雜度必須是 O(log n) 級別。
如果數組中不存在目標值,返回 [-1, -1]。
示例 1:
輸入: nums = [5,7,7,8,8,10], target = 8
輸出: [3,4]
示例 2:
輸入: nums = [5,7,7,8,8,10], target = 6
輸出: [-1,-1]
思路
- 看到這道題很容易想到使用二分查找
- 但是難點在於,不僅要找到這個值的下標,還要找起始下標和終結下標
- 其實單純使用兩個二分查找即可,一個找起始下標,一個找最終下標
- 你可能會疑惑,這樣會不會造成太多冗餘計算?
- 但實際上,O(logn)和O(2logn)是相等的,都是O(logn)的時間複雜度。
- 所以使用兩次二分查找的方式可以減少你的思考方式,同時不會增加複雜度。
二分查找細節
- 首先初始化l,r,左右指針
- 循環條件設爲當l<r,這樣設立的原因是跳出循環的時候l與r總是相等的,不用思考是l還是r
- 取中值,取中左還是中右,這就需要看情況了。
- 如果我們想要找值的起始下標,那麼當mid對應值大於這個值或者哪怕當找到這個值的時候,右邊界都要縮小
- 即當nums[mid] >= target的時候,r = mid
- 同時要注意不能以mid-1這樣的形式縮小,否則當找到這個起始下標的時候還得再減1,就不對了。
- 因爲不可以左右兩個都=mid,必須有一個=mid+1,否則會陷入死循環。
- 所以在其他情況下,左邊界等於mid + 1
- 同時,mid取左中還是右中,要以你選擇哪個是mid+1來定。這裏我們選了mid+1的是左邊,所以我們要取左中值。否則會陷入死循環。
所以可以這樣寫出代碼
while l < r:
mid = (l + r) // 2
if nums[mid] >= target:
r = mid
else:
l = mid + 1
對於取結束下標,也是如此。
class Solution:
def searchRange(self, nums, target):
# 取起始下標
l, r = 0, len(nums) - 1
while l < r:
mid = (l + r) // 2
if nums[mid] >= target:
r = mid
else:
l = mid + 1
# 沒找到
if not nums or nums[l] != target:
return [-1,-1]
# 取結束下標
a, b = l, len(nums) - 1
while a < b:
mid = (a + b + 1) // 2
if nums[mid] <= target:
a = mid
else:
b = mid - 1
return [l,a]