LeetCode二分查找算法應用

二分查找的概念

寫這個總結的初衷是4月29日每日一題中「山脈數組中查找目標值」,其中力扣題解對於二分查找做了總結和講解,感覺講的非常好,所以也想記錄下來。
一般題中給出O(logN)時間複雜度或是在有序數組中進行查找,我們通常都會想到二分查找,其實二分查找不一定只能應用於有序數組中,也可以應用於部分有序、旋轉排序數組、山脈數組中。
二分查找是減而治之思想的典型應用:

  • 每次都將問題的規模減少,直到問題解決
  • 減治思想是分治思想的特例
  • 減治思想的應用:雙指針問題、選擇排序算法、TopK問題、二分搜索樹中的查找操作

思路1,在循環體中查找元素

# -*- encoding: utf-8 -*-
"""
@File    : binary_search.py
@Time    : 2020/4/30 2:46 下午
@Author  : zhengjiani
@Email   : [email protected]
@Software: PyCharm
"""
def search(nums,left,right,target):
# 在[left,right]中查找
    while left<right:
    # java等語言中爲防止整型溢出,改爲mid = left + (right - left)/2
        mid = (left + right) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] > target:
            # 下一輪搜索區間[left,mid-1],向左區間搜索
            right = mid - 1
        else:
            # 下一輪搜索區間[mid+1,right],向右區間搜索
            left = mid + 1
    return -1
if __name__ == '__main__':
    nums = [0,1,2,3,4,5]
    print(search(nums,0,len(nums),5))

思路1特點:

  • 在區間中只剩一個元素的時候還會執行一遍循環體
  • 在循環體內部找目標元素
  • 循環體內部的分支通常有3個

在循環體中縮小搜索區間

def search(nums,left,right,target):
    while left<right:
        # 選擇中位數時下取整
        mid = left + (right-left)//2
        if check(mid):
            left = mid + 1
        else:
            right = mid - 1
def search_3(nums,left,right,target):
    while left<right:
        # 選擇中位數時上取整
        mid = left + (right-left+1)//2
        if check(mid):
            right = mid - 1
        else:
            left = mid + 1

思路2特點:

  • 每個分支做的事情都是根據在不斷縮小目標元素的區間
  • 當退出循環以後,區間只剩下一個元素,視情況單獨判斷m

1095題:山脈數組中查找目標值

prac1095

# """
# This is MountainArray's API interface.
# You should not implement it, or speculate about its implementation
# """
#class MountainArray:
#    def get(self, index: int) -> int:
#    def length(self) -> int:

def binary_search(mountain, target, l, r, key=lambda x: x):
    target = key(target)
    while l <= r:
        mid = (l + r) // 2
        cur = key(mountain.get(mid))
        if cur == target:
            return mid
        elif cur < target:
            l = mid + 1
        else:
            r = mid - 1
    return -1

class Solution:
    def findInMountainArray(self, target: int, mountain_arr: 'MountainArray') -> int:
        l, r = 0, mountain_arr.length() - 1
        # 尋找峯頂
        while l < r:
            mid = (l + r) // 2
            if mountain_arr.get(mid) < mountain_arr.get(mid + 1):
                l = mid + 1
            else:
                r = mid
        peak = l
        index = binary_search(mountain_arr, target, 0, peak)
        if index != -1:
            return index
        index = binary_search(mountain_arr, target, peak + 1, mountain_arr.length() - 1, lambda x: -x)
        return index

力扣相關題目

這些題目的區別僅在於對於二分查找中判別函數的設計上:

33、81題:搜索旋轉排序數組I、II

prac33
採用思路1三分支

# -*- encoding: utf-8 -*-
"""
@File    : prac33.py
@Time    : 2020/4/27 8:20 上午
@Author  : zhengjiani
@Email   : [email protected]
@Software: PyCharm
通過比較端點數據查看子數組是否有序
"""
from typing import List


class Solution:
    """時間複雜度O(logN)"""
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        l,r =0,len(nums)-1
        while l<=r:
            mid = (l + r)//2
            if nums[mid] == target:
                return mid
            # 端點比較驗證子數組是否爲有序數組
            if nums[0] <= nums[mid]:
                if nums[0] <= target < nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            # 無序
            else:
                if nums[mid] < target <= nums[len(nums)-1]:
                    l = mid + 1
                else:
                    r = mid - 1
        return -1


if __name__ == '__main__':
    # nums = [4,5,6,7,0,1,2]
    # target = 0
    nums = [3,1]
    target = 1
    s = Solution()
    print(s.search(nums,target))

153、154題:尋找旋轉排序數組中的最小值I、II

69題:平方根

287題:尋找重複數

875題:愛吃香蕉的珂珂

1300題:轉變數組後最接近目標值的數組和

410題:分割數組最大值

其他題還沒做到,等做到再添加進來,就醬~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章