34 在排序數組中查找元素的第一個和最後一個位置

給定一個按照升序排列的整數數組 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]
思路
  1. 看到這道題很容易想到使用二分查找
  2. 但是難點在於,不僅要找到這個值的下標,還要找起始下標和終結下標
  3. 其實單純使用兩個二分查找即可,一個找起始下標,一個找最終下標
  4. 你可能會疑惑,這樣會不會造成太多冗餘計算?
  5. 但實際上,O(logn)和O(2logn)是相等的,都是O(logn)的時間複雜度。
  6. 所以使用兩次二分查找的方式可以減少你的思考方式,同時不會增加複雜度。

二分查找細節

  1. 首先初始化l,r,左右指針
  2. 循環條件設爲當l<r,這樣設立的原因是跳出循環的時候l與r總是相等的,不用思考是l還是r
  3. 取中值,取中左還是中右,這就需要看情況了。
  4. 如果我們想要找值的起始下標,那麼當mid對應值大於這個值或者哪怕當找到這個值的時候,右邊界都要縮小
  5. 即當nums[mid] >= target的時候,r = mid
  6. 同時要注意不能以mid-1這樣的形式縮小,否則當找到這個起始下標的時候還得再減1,就不對了。
  7. 因爲不可以左右兩個都=mid,必須有一個=mid+1,否則會陷入死循環。
  8. 所以在其他情況下,左邊界等於mid + 1
  9. 同時,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]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章