最近也在進行一些面試嘛,也見識到了不少各類各樣的題目,其中就有一些和二分查找相關的.python
二分查找,在有序的數組中快速找到目標值.面試
這個算法在上學的時候學過,以後就沒有看過了,由於比較"簡單"嘛~.算法
然而在面試過程當中,我在二分查找及相似題目上栽了三次...數組
因此今天作一個總結.學習
注意:下文的代碼中沒有進行參數校驗,實際使用時須要進行參數校驗spa
class Solution:
def binary_search(self,arr,target):
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] > target:
high = mid - 1
else:
low = mid + 1
return
if __name__ == "__main__":
c = Solution()
print(c.binary_search([1,2,3,4,5,6],4))
複製代碼
我大部分都載在這裏了,,,,code
當數組中有重複值的時候,返回該值第一個出現或者最後一個出現的下標.get
好比[1,2,2,2,2,2,3]
,若是返回第一個出現的地方,應該返回1
,若是返回最後一個,應該返回5
.博客
首先咱們能夠想到,能夠經過二分法拿到某一個目標值
,而後向左或者向右遍歷找到邊界.string
這個算法的時間複雜度是O(n)
,在最壞狀況下,即整個數組的值相同時,算法時間消耗爲n/2
,仍是O(n)
是不太使人滿意的.
那麼有沒有更好的辦法呢?有的,仍是二分法,二分法在找到目標值時,進行了返回,咱們能夠不讓其返回,繼續查找下一個便可.
代碼實現以下:
查找最左目標值
def binary_search2(self ,arr , target):
low = 0
high = len(arr) - 1
while low < high:
mid = (low + high) // 2
if arr[mid] >= target:
high = mid
else:
low = mid + 1
if arr[high] == target:
return high
return -1
複製代碼
在這個實現中,當找到某個目標值時,按照原二分法中的大於處理
,由於咱們想找到的是最左目標值.
注意,這個方法和上面的二分法有幾處差別.
首先當找到的值大於等於目標值時,high=mid
,由於此時的值可能就是最左目標值,所以須要將當前值繼續保留在下一次查找的區間內.
而當小於的時候,那麼確定不是最終目標,採用low = mid + 1
來處理,同時這一步能夠保證不會出現死循環.(若是low= mid
,在[0,1]
中查找1
的時候會陷入死循環.low=0,high=1,mid=0.arr[mid]<target,low=mid=0,通過一次循環,low和high無變化,陷入死循環.
).
稍加修改能夠實現尋找最右的值.
def binary_search3(self ,arr , target):
low = 0
high = len(arr) - 1
while low < high:
mid = (low + high) // 2 + 1
if arr[mid] <= target:
low = mid
else:
high = mid - 1
if arr[high] == target:
return high
return -1
複製代碼
注意在第五行,mid = ( low + high )// 2 + 1
這裏和上面不同,添加了+1
,由於當在[0,1]尋找0時會陷入死循環
.
上面兩個其實實現的只是最左/最右
的區別,所以能夠合併一下,成爲一個方法.
# flag=0,獲取最左目標,不然獲取最後目標
def binary_search4(self ,arr , target, flag):
low = 0
high = len(arr) - 1
while low < high:
mid = (low + high) // 2
if arr[mid] == target:
if flag == 0:
high = mid
else :
low = mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
if arr[high] == target:
return high
return -1
複製代碼
其實咱們能夠發現,當想要獲取最左最右的時候,區別對待的只是當前值=目標值
這一個邏輯,所以咱們將相等的邏輯單獨寫,而且根據flag
的值進行不一樣的處理(分別任high或者low等於mid).而小於和大於的邏輯都是不變的,進行減一或者加一操做,將當前值從待查區間去掉.
二叉查找你們都清楚,那麼變種查最左或者最右,其實只須要將相等的邏輯考慮爲大於/小於
便可,以後在循環結束後比較當前的index的值是不是目標值就可.剩下的就是代碼中一些小狀況,尤爲是死循環
的處理也要注意一下.
其實不要以爲二分法查找太簡單沒有用,我原來也是這樣想的...後來看了一些文章,才知道二叉樹的應該之普遍,好比下面參考連接中的文章.
我遇到的面試題目呢,主要有兩個,一個就是很直接讓你查找目標值的最左或者最右,另外一個就是須要你查出目標值的區間起始下標.
這個時候若是你能寫出上面這個方法,調用兩次便可返回結果,豈不是美滋滋.
完。
以上皆爲我的所思所得,若有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文連接。
聯繫郵箱:huyanshi2580@gmail.com
更多學習筆記見我的博客------>呼延十