標籤:

二分法查找及變種分析

基於Python3

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

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

  1. nums[middle] > target
  2. nums[middle] < targetnums[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 -1if __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 -1if __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

推薦閱讀:

Shannons Switching Game
RSA演算法詳解
Search in Rotated Sorted Array的升級
基於深度學習的廣告CTR預估演算法
自動駕駛還在盯著「演算法」和「感測器」么?風河打算為其開發通用底層操作系統了

TAG:演算法 |