【演算法】二分查詢

九年义务漏网鲨鱼發表於2024-11-15

基本內容

提高在有序的陣列中查詢滿足某一條件的索引

  • 二分查詢的基本型別

    ① 有多種情況滿足條件,找到滿足條件的最右索引,例如找到值為4的最右索引(也可以換為小於5的最後一個元素)

​ ② 有多種情況滿足條件,找到滿足條件的最左索引,例如找到大於4的第一個元素...

​ ③ 僅存在一種滿足條件的情況,①、②程式碼都適用

[!note]

可以發現,針對①、②兩種情況,可以有不同的問法,例如在②情況中,也可以適用於找到4的最後一個元素,只需要在找到的索引上減一即可找到

  • 基本模板

① 情況

def binary_search(self, l, r, target): 
    while l < r:
        mid = (l + r + 1) // 2  
        if 滿足條件:
            l = mid # 因為可能是最右情況,所以要保持不變,不能是 mid + 1, 又因為是需要找到最右情況,所以需要透過l往r逼近
        else:
            r = mid - 1
    return l

② 情況

def binary_search(self, l, r, target): 
    target = l
    while l < r:
        mid = (l + r) // 2  
        if 滿足條件:
            r = mid #需要找到最左的情況,所以需要透過r往l逼近
        else:
            l = mid + 1 
    return l
  • 入門例子

查詢有序數列中值為4的最後一個元素,[1,3,4,4,4,4,6,8,9]

​ 可以用①、②兩種程式碼解決

條件為小於等於4為情況①

def binary_search(self, l, r, target): 
    while l < r:
        mid = (l + r + 1) // 2  
        if array[mid] <= 4:
            l = mid 
        else:
            r = mid - 1
    return l

條件為大於4為情況②,只需在最後輸出的時候進行減一操作

def binary_search(self, l, r, target): 
    target = l
    while l < r:
        mid = (l + r) // 2  
        if array[mid] > 4:
            r = mid
        else:
            l = mid + 1 
    return l - 1 # 因為找到的是大於4的第一個元素6,所以還需要減一操作

題目

二分查詢往往需要和其他型別的演算法結合,所以題目所需涉及的內容不只是二分查詢

  1. 3261. 統計滿足 K 約束的子字串數量 II

滑動視窗 字首和 二分查詢

class Solution:
    def __init__(self):
        self.lefts = None
        self.pre = None
    def binary_search(self, l, r):
        target = l
        while l < r:
            mid = (l + r) // 2  
            if self.lefts[mid] > target:
                r = mid
            else:
                l = mid + 1
        return l - 1
    def countKConstraintSubstrings(self, s: str, k: int, queries: List[List[int]]) -> List[int]:
        self.pre = [0] * (1 + len(s))
        self.lefts = [-1] * len(s)
        left = 0
        cnt = [0, 0]
        ans = []
        for right, x in enumerate(s):
            cnt[ord(x) % 2] += 1
            while cnt[0] > k and cnt[1] > k: 
                cnt[ord(s[left]) % 2] -= 1
                left += 1
            
            self.lefts[right] = left
            self.pre[right + 1] = self.pre[right] + (right - left + 1)

        for i,j in queries:
            if i > self.lefts[j] :
                ans.append( (j - i + 2)*( j -i + 1)//2)
            else:
                h = self.binary_search(i, j)
                ans.append(self.pre[j+1] - self.pre[h+1] + (h-i + 2)*(h-i +1)//2)
        return ans

相關文章