基於Python3
普通二分法查找目標值的index
二分法查找的思路很簡單,先確定好列表nums的一頭start一尾end,中間值middle根據頭尾數值之和用地板除法除以2,即(start + end) // 2。將目標值target與nums[middle]進行比對,這時候有3種結果:
- nums[middle] > target
- nums[middle] < target
- 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語句進行分析,
- nums[middle] > target,所以賦值end=middle-1,這時end且又和start相等
- 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語句進行分析,
- nums[middle] > target,所以賦值end=middle-1,這時end且又和start相等
- 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語句進行分析,
- nums[middle] < target,所以賦值start=middle+1,這時start且又和end相等,此時start=end,跳出循環,判斷nums[start] == target,需要return start。
- 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
