二分查找【python總結】

將原本是線性時間提升到了對數時間log(N)范圍,大大縮短了搜索時間

前提,必須在有序數據中進行查找。

1. 最基本的二分查找

leetcode參考[35]:Search Insert Position

劍指offer:數字在排序數組中出現的次數

def binarySearch(A, target):
    low,high = 0,len(A)-1
    while low <= high:
        mid = low + (high - low) // 2
        if A[mid] == target:
            return mid
        elif A[mid] > target:
            high = mid - 1
        else:
            low = mid + 1
    return -1

其中,有幾個要注意的點:

  1. 循環的判定條件是:low <= high
  2. 為了防止數值溢出,mid = low + (high - low)/2
  3. A[mid]不等于target時,high = mid - 1low = mid + 1

2. 查找目標值區域的邊界

Leetcode參考[34]: Find First and Last Position of Element in Sorted Array

劍指offer:數字在排序數組中出現的次數

2.1 查找目標值區域的左邊界/查找與目標值相等的第一個位置/查找第一個不小于目標值數的位置

A = [1,3,3,5, 7 ,7,7,7,8,14,14]
target = 7
return 4

def binarySearchLowerBound(A,  target):
    low,high = 0,len(A)-1
    while low <= high:
        mid = low + (high - low) // 2
        if target <= A[mid]:
            high = mid - 1
        else:
            low = mid + 1
    if low < A.length and A[low] == target:
        return low
    else
        return -1

2.2 查找目標值區域的右邊界/查找與目標值相等的最后一個位置/查找最后一個不大于目標值數的位置

A = [1,3,3,5,7,7,7, 7 ,8,14,14]
target = 7
return 7

def binarySearchUpperBound(A,  target):
    low,high = 0,len(A)-1
    while low <= high:
        mid = low + (high - low) // 2
        if target >= A[mid]:
            low = mid + 1
        else:
            high = mid - 1
    if high >-1 and A[low] == target:
        return high
    else:
        return -1

此題以可變形為查找第一個大于目標值的數/查找比目標值大但是最接近目標值的數,我們已經找到了最后一個不大于目標值的數,那么再往后進一位,返回high + 1,就是第一個大于目標值的數。

2.3 查找最后一個小于目標值的數/查找比目標值小但是最接近目標值的數

此題以可由第 2 題變形而來,我們已經找到了目標值區域的下(左)邊界,那么再往左退一位,即low - 1,就是最后一個小于目標值的數。其實low - 1也是退出循環后high的值,因為此時 high剛好等于low - 1,它小于low,所以 while 循環結束。我們只要判斷high是否超出邊界即可。

A = [1,3,3, 5 ,7,7,7,7,8,14,14]
target = 7
return 3

def binarySearchLowerBound2(A,  target):
    low,high = 0,len(A)-1
    while low <= high:
        mid = low + (high - low) // 2
        if target > A[mid]:
            low = mid + 1
        else:
            high = mid - 1
    if high >-1:
        return high
    else:
        return -1

2.4 查找第一個大于目標值的數/查找比目標值大但是最接近目標值的數

此題以可由第 3 題變形而來,我們已經找到了目標值區域的上(右)邊界,那么再往右進一位,即high + 1,就是第一個大于目標值的數。其實high + 1也是退出循環后low的值,因為此時 low剛好等于high + 1,它大于high,所以 while 循環結束。我們只要判斷low是否超出邊界即可。

A = [1,3,3,5,7,7,7,7, 8 ,14,14]
target = 7
return 8

def binarySearchUpperBound2(A,  target):
    low,high = 0,len(A)-1
    while low <= high:
        mid = low + (high - low) // 2
        if target >= A[mid]:
            low = mid + 1
        else:
            high = mid - 1
    if high < len(A):
        return high
    else:
        return -1

3. 在旋轉數組中查找最小元素

3.1 查找旋轉數組的最小元素(假設不存在重復數字)

LeetCode[153]: Find Minimum in Rotated Sorted Array
Input: [3,4,5,1,2]
Output: 1

def findMin(nums):
    if len(nums) == 0
        return -1
    left,right = 0,len(nums) - 1
    while left < right:
        mid = left + (right - left) // 2
        if nums[mid] > nums[right]:
            left = mid + 1
        else:
            right = mid
    return nums[left]

注意這里和之前的二分查找的幾點區別:

  1. 循環判定條件為left < right,沒有等于號
  2. 循環中,通過比較nums[left]與num[mid]的值來判斷mid所在的位置:
  • 如果nums[mid] > nums[right],說明前半部分是有序的,最小值在后半部分,令left = mid + 1
  • 如果nums[mid] <= num[right],說明最小值在前半部分,令right = mid

最后,left會指向最小值元素所在的位置。

3.2 查找旋轉數組的最小元素(存在重復項)

LeetCode[154]: Find Minimum in Rotated Sorted Array II
劍指offer:旋轉數組的最小數字
Input: [2,2,2,0,1]
Output: 0

def findMin(nums):
    if len(nums) == 0
        return -1
    left,right = 0,len(nums) - 1
    while left < right:
        mid = left + (right - left) // 2
        if nums[mid] > nums[right]:
            left = mid + 1
        elif nums[mid] < nums[right]:
            right = mid
        else:
            right-=1
    return nums[left]

和之前不存在重復項的差別是:當nums[mid] == nums[right]時,我們不能確定最小值在 mid的左邊還是右邊,所以我們就讓右邊界減一。

4. 在旋轉排序數組中搜索

4.1 在旋轉排序數組中搜索并返回目標元素的下標(不考慮重復項)

LeetCode[33]: Search in Rotated Sorted Array

法一:

  • 先利用方法 3.1 查找數組中的最小元素,即確定分界點的位置
  • 把旋轉的數組當成偏移,用(offset + mid) % len來求真實的 mid 的位置。
  • 然后用二分查找來定位目標值
def search(self, nums: List[int], target: int) -> int:
    lo,hi=0,len(nums)-1
    while lo<hi:
        mid=(lo+hi)//2
        if nums[mid]>nums[hi]:
            lo=mid+1
        else:
            hi=mid 
    offset =lo #lo==hi is the index of the smallest value
    lo,hi=0,len(nums)-1
    while lo<=hi:
        mid=lo+(high-lo)//2
        realmid=(mid+offset)%len(nums)
        if nums[realmid]==target:
            return realmid
        if nums[realmid]<target: 
            lo=mid+1
        else:
            hi=mid-1
    return -1

法二:其實沒有必要找到旋轉數組的分界點,對于搜索左側還是右側我們是可以根據mid跟high的元素大小來判定出來的,直接根據target的值做二分搜索就可以了。

def search(self, nums: List[int], target: int) -> int:
    if len(nums) == 0:
        return -1
    left,right=0,len(nums)-1
    while left<=right:
        mid=left+(right-left)//2
        if nums[mid] == target:
            return mid
        elif nums[left] <= nums[mid]:
            if target < nums[mid] and target >= nums[left]:
                right = mid - 1
            else:
                left = mid + 1
        elif nums[mid] <= nums[right]:
            if target > nums[mid] and target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    return -1

4.2 在旋轉排序數組中搜索并返回目標元素的下標(考慮重復項)

LeetCode: Search in Rotated Sorted Array II

def search(self, nums: List[int], target: int) -> int:
    if len(nums) == 0:
        return -1
    left,right=0,len(nums)-1
    while left<=right:
        mid=left+(right-left)//2
        if nums[mid] == target:
            return mid
        elif nums[mid] > nums[right]:
            if target < nums[mid] and target >= nums[left]:
                right = mid 
            else:
                left = mid + 1
        elif nums[mid] < nums[right]:
            if target > nums[mid] and target <= nums[right]:
                left = mid + 1
            else:
                right = mid
        else:
            right-=1
    return -1

5. 二維數組中的查找

劍指offer:二維數組中的查找

二維數組是有序的,從右上角來看,向左數字遞減,向下數字遞增。因此可以利用二分查找的思想,從右上角出發:

  • 當要查找數字比右上角數字大時,下移;
  • 當要查找數字比右上角數字小時,左移;

【參考】
作者:繁著
鏈接:http://www.lxweimin.com/p/0f823fbd4d20

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373