Python二分法查找及變種分析


基於Python3

普通二分法查找目標值的index

二分法查找的思路很簡單,先確定好列表nums的一頭start一尾end,中間值middle根據頭尾數值之和用地板除法除以2,即(start + end) // 2。將目標值target與nums[middle]進行比對,這時候有3種結果:

  1. nums[middle] > target
  2. nums[middle] < target
  3. nums[middle] = target

以上3種情況前2種不斷循環,直到滿足第3種跳出循環。

情況1

說明target在前半段,所以end=middle-1

情況2

說明target在后半段,所以start=middle+1

情況3

說明target就是nums[middle],所以返回middle,這是跳出循環的方式之一
所以到此可以寫出循環體中的代碼,如下:

       middle = (start + end) // 2
    		if target > nums[middle]:
    			start = middle + 1
    		elif target < nums[middle]:
    			end = middle - 1
    		else:
    			return middle

但是,萬一一直達不到第3種的條件呢?即target壓根不在列表nums中,這就出現了情況4

情況4

我們循環到最后階段一定是start=middle-1=end-2,即start與middle與end緊挨着。這時候,我們將start,middle,end代入if語句進行分析,

  1. nums[middle] > target,所以賦值end=middle-1,這時end且又和start相等
  2. nums[middle] < target,所以賦值start=middle+1,這時start且又和end相等

那么此時還有最后一個值需要判斷,以上2種情況再代入循環體,此時有start=end=middle。若沒有nums[middle]=target,則跳出循環(此為跳出循環的方式之二)返回-1
很明顯,start與end一直互相逼近,但是start一直小於end的,要不要加上等於這個條件呢?別忘了只有start=end,才能夠對列表中的最后一個元素進行判斷。

完整代碼如下:

def binarySearch(nums, target):
    	start = 0
    	end = len(nums) -1
    	while start <= end:
    		middle = (start + end) // 2
    		if target > nums[middle]:
    			start = middle + 1
    		elif target < nums[middle]:
    			end = middle - 1
    		else:
    			return middle
    	return -1

if __name__ == "__main__":
    	List = [1,4,4,5,7,7,8,9,9,10]
    	print(List)
    	print(binarySearch(List, 1))
    	
# output
# [1, 4, 4, 5, 7, 7, 8, 9, 9, 10]
# 0

變種二分法查找

給定一個排序的整數數組(升序)和一個要查找的整數target,用O(logn)的時間查找到target第一次出現的下標(從0開始),如果target不存在於數組中,返回-1。

普通的二分查找只要找到target就行了,這個必須考慮到列表中重復的元素。
也就是說,即便找到了也不能跳出循環體。那么怎樣才能跳出循環體呢?
別忘了跳出循環的方式有2種,剛才只是封住了第1種,還有第2種呢。即while的設置條件。很明顯,start小於end是必須的循環條件的,而start大於end肯定是跳出循環的條件,那么start=end是跳出還是不跳出呢?首先要清楚start=end的意義是什么,即經過迭代此時列表nums中的選擇區域變成了一個元素,循環的作用是將這最后一個元素選出來。
跳出循環,去判斷是哪種可能。這個元素只有2種可能:不滿足target或者是滿足target的第一個元素。

可能一:不滿足target的過程

注意,和普通二分法情況一樣,循環到最后階段一定是start=middle-1=end-2,即start與middle與end緊挨着。這時候,我們將start,middle,end代入if語句進行分析,

  1. nums[middle] > target,所以賦值end=middle-1,這時end且又和start相等
  2. nums[middle] < target,所以賦值start=middle+1,這時start且又和end相等
    此時start=end,跳出循環,判斷不滿足target,需要return -1

可能二:滿足target的第一個元素的過程

如何確定是滿足target的第一個元素?
先明白循環中找到第一個等於target的nums[middle]元素不跳出循環應該做什么。很明顯,應該搜尋nums[start:middle]區域,所以要賦值end=middle,即搜尋新的nums[start:end]區域。再次循環,此時只剩下2種結果,再找到等於target的nums[middle] 或者 找不到了

結果1再找到等於target的nums[middle]

  • 第一步得到middle = (start + end) // 2
  • 有target = nums[middle]
  • 賦值end=middle
  • 進入新的循環,搜尋新的nums[start:end]區域,依然面臨2種結果

結果2再找不到等於target的nums[middle]

  • 第一步得到middle = (start + end) // 2
  • 一定有nums[middle] < target而不是nums[middle] > target,因為nums是遞增的。
  • 此時start = middle + 1
  • 進入新的循環,搜尋新的nums[start:end]區域,依然面臨2種結果

以上2種結果互相交錯,循環到最后階段一定是start=middle-1=end-2,即start與middle與end緊挨着。這時候,我們將start,middle,end代入if語句進行分析,

  1. nums[middle] < target,所以賦值start=middle+1,這時start且又和end相等,此時start=end,跳出循環,判斷nums[start] == target,需要return start。
  2. nums[middle] = target,所以賦值end=middle,得到start=middle-1,再帶入循環,有middle = (start + end) // 2,得到start=middle=end-1。
    此時最后一步又分為二,1若nums[middle] < target,所以賦值start=middle+1,這時start且又和end相等,此時start=end,跳出循環,判斷nums[start] == target,需要return start。2若nums[middle] = target,所以賦值end=middle,得到start=middle=end,跳出循環,判斷nums[start] == target,需要return start。
def binarySearch(nums, target):
    	start = 0
    	end = len(nums) -1
    	while start < end:
    		middle = (start + end) // 2
    		if target > nums[middle]:
    			start = middle + 1
    		elif target < nums[middle]:
    			end = middle - 1
    		else:
    			end = middle
    	if nums[start] == target:
    		return start 
    	return -1

if __name__ == "__main__":
    	List = [1,4,4,5,7,7,8,9,9,10]
    	print(List)
    	print(binarySearch(List, 4))
    	
# output
# [1, 4, 4, 5, 7, 7, 8, 9, 9, 10]
# 1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM