1. 程式人生 > 其它 >LeetCode Q33-Q35練習筆記 (Python3) 二分法查詢

LeetCode Q33-Q35練習筆記 (Python3) 二分法查詢

技術標籤:二分法python演算法leetcode

LeetCode Q33-Q35

Q33 搜尋旋轉排序陣列 Search in Rotated Sorted Array

升序排列的整數陣列 nums 在預先未知的某個點上進行了旋轉(例如, [0,1,2,4,5,6,7] 經旋轉後可能變為 [4,5,6,7,0,1,2] )。

請你在陣列中搜索 target ,如果陣列中存在這個目標值,則返回它的索引,否則返回 -1 。

這道題我是根本沒有看懂啊,看到很多評論是用二分法做的,但是由於這道題也沒有要求時間複雜度O(logn),我就用自己的方法寫了

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        return nums.index(target) if target in nums else -1

這種使用內建函式的當然是不對的。。。

Q34 在排序陣列中查詢元素的第一個和最後一個位置 Find First And Last Position of Element in Sorted Array

給定一個按照升序排列的整數陣列 nums,和一個目標值 target。找出給定目標值在陣列中的開始位置和結束位置。
如果陣列中不存在目標值 target,返回 [-1, -1]。
進階:
你可以設計並實現時間複雜度為 O(log n) 的演算法解決此問題嗎?

這道題既然提出了時間複雜度的要求,那麼就是要求使用二分法來做這道題。我也很久沒有使用過這個方法了,好好的複習了一下。二分法大致是用在已經排序過的陣列中,通過判斷中間值與目標值的大小來分割判斷區域,從而快速的查詢到目標值。由於這道題需要找兩個邊界值,那麼就需要用到兩次二分法,時間複雜度為O(log2n).

下面這段程式碼是我根據一個java程式碼修改的,但是我並不是很能理解他的思路

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        lst=[-1,-1]
        left=0
        right=len(nums)-1

        if not nums:
            return lst

        while left<right: #第一輪查詢
            mid=(left+right)//2 #對於python兩種寫法都是可以的
            if nums[mid]>=target: #找左邊界,即位置最低的target
                right=mid #由於可以等於target的時候查詢,所以分邊界可以讓right等於mid
            else:
                left=mid+1

        if nums[left]==target: #判斷是否查詢到了目標值,如果沒有就直接返回[-1,-1]
            lst[0],lst[1]=left,left
        else:
            return lst
		#此時left與right已經發生了改變
        right=len(nums) #此時left沒有改變,仍然是作為左邊界的索引
        while left<right: #第二輪查詢
            mid = (left+right)//2
            if nums[mid]<=target:
                left=mid+1
            else:
                right=mid
        lst[1]=left-1

        return lst

這道題不能幹寫,需要明白的是查詢的第一個左邊界指的是第一個等於target的數的位置右邊界指的是第一個大於target的數的位置減一,當然,這一切的前提在於題目提供的是升序陣列,若是倒序或者是亂序就另當別論了

我在題解裡面看到了一個人用python寫的,感覺更加容易理解。他先是將二分法作為函式然後呼叫便可以了,最後a的結果就是第一個等於target的數的位置,b的結果就是第一個大於target的數的位置,所以最後答案需要將b-1

class Solution(object):
    def searchRange(self,nums, target):
        
        def left_func(nums,target):#二分法函式
            n = len(nums)-1
            left = 0
            right = n
            while(left<=right):
                mid = (left+right)//2
                if nums[mid] >= target:
                    right = mid-1
                if nums[mid] < target:
                    left = mid+1
            return left
            
        a =  left_func(nums,target)
        b = left_func(nums,target+1)
        
#當索引值等於陣列長度意味著該數比陣列中所有數都大(因為索引最大值為len(nums)-1),也就不會存在於陣列中
        if  a == len(nums) or nums[a] != target: 
            return [-1,-1]
        else:
            return [a,b-1]

但是看完這兩種方法的程式碼我又暈了,為什麼在二分法查詢階段,第一個程式碼中是讓right=mid,而第二個程式碼中是讓right=mid-1?之後發現原因出在他們的while迴圈條件,如果條件是<,那麼便是right=mid;如果條件是<=,那麼便是right=mid-1

同時,這兩種方法中的二分法while迴圈時並沒有return任何值,這就會導致迴圈會一直持續,直到最後所查詢的索引值為left

Q35 搜尋插入位置

給定一個排序陣列和一個目標值,在陣列中找到目標值,並返回其索引。如果目標值不存在於陣列中,返回它將會被按順序插入的位置。
你可以假設陣列中無重複元素。

這道題基本就是最基礎的二分法查找了,程式碼如下:

def binarySearch(nums, target):
    left=0
    right=len(nums)-1
    while left<right:
        mid=(left+right)//2
        if nums[mid]>target: #說明目標值target在nums[mid]左邊,將右邊界縮小
            right=mid
        elif nums[mid]<target: #說明目標值target在nums[mid]右邊,將右邊界增大
            left = mid + 1
        else:
            return mid #返回查詢的目標值在陣列中的索引
    return left

Q33 - Q35 一些筆記

  1. 二分法基本程式碼:
def binarySearch(nums, target):
    left=0
    right=len(nums)-1
    while left<right:
        mid=(left+right)//2
        if nums[mid]>target: #說明目標值target在nums[mid]左邊,將右邊界縮小
            right=mid
        elif nums[mid]<target: #說明目標值target在nums[mid]右邊,將右邊界增大
            left = mid + 1
        else:
            return mid #返回查詢的目標值在陣列中的索引
  1. 二分法的while迴圈條件中,如果條件是<,那麼便是right=mid;如果條件是<=,那麼便是right=mid-1
  2. 二分法while迴圈時如果沒有return任何值,這就會導致迴圈會一直持續,直到最後所查詢的索引值為left
  3. mid=(left+right)//2mid=left+(right-left)//2都行,注意python中一定要用到//整除