[面試高頻]詳解二分查找

前言

最近也在進行一些面試嘛,也見識到了不少各類各樣的題目,其中就有一些和二分查找相關的.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的值是不是目標值就可.剩下的就是代碼中一些小狀況,尤爲是死循環的處理也要注意一下.

其實不要以爲二分法查找太簡單沒有用,我原來也是這樣想的...後來看了一些文章,才知道二叉樹的應該之普遍,好比下面參考連接中的文章.

我遇到的面試題目呢,主要有兩個,一個就是很直接讓你查找目標值的最左或者最右,另外一個就是須要你查出目標值的區間起始下標.

這個時候若是你能寫出上面這個方法,調用兩次便可返回結果,豈不是美滋滋.

參考連接

hedengcheng.com/?p=595

完。




ChangeLog

2019-03-17 完成

以上皆爲我的所思所得,若有錯誤歡迎評論區指正。

歡迎轉載,煩請署名並保留原文連接。

聯繫郵箱:huyanshi2580@gmail.com

更多學習筆記見我的博客------>呼延十

相關文章
相關標籤/搜索