二分查找边界总搞错?四种写法简单易懂!

243 阅读4分钟

前言

二分查找想必我们都老生常谈,但是每次在讨论边界时总搞错到底什么时候mid+1什么时候用<=,我本人也曾在此多次头疼过,所以我为大家整理出了四种常见的区间写法:闭区间写法、左闭右开区间写法、开区间写法和左开右闭区间写法。这四种写法主要在处理边界情况时略有差异,但主要核心思想是一致的。

一、闭区间写法

在闭区间写法中,将搜索范围被表示为一个闭区间 [left, right],即包含左右两端点。我们通过不断调整左右边界来逼近目标值,直至最终找到目标或确定目标不存在:

def binarySearch1(nums, target):
    left, right = 0, len(nums) - 1  # 闭区间 [left, right]
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1  # 范围缩小到 [mid+1, right]
        else:
            right = mid - 1  # 范围缩小到 [left, mid-1]
    return left

二、左闭右开区间写法

左闭右开区间写法使用半开半闭区间 [left, right),即左侧闭合而右侧开放。在这种写法中,循环条件和边界处理稍有不同:

def binarySearch2(nums, target):
    left, right = 0, len(nums)  # 左闭右开区间 [left, right)
    while left < right:
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1  # 范围缩小到 [mid+1, right)
        else:
            right = mid  # 范围缩小到 [left, mid)
    return left

三、开区间写法

开区间写法使用开区间 (left, right),即左右两端点都不包含在内。这种写法在处理边界时需要特别注意,循环条件和边界更新也略有不同:

def binarySearch3(nums, target):
    left, right = -1, len(nums)  # 开区间 (left, right)
    while left + 1 < right:
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid  # 范围缩小到 (mid, right)
        else:
            right = mid  # 范围缩小到 (left, mid)
    return right

四、左开右闭区间写法

左开右闭区间 (left, right],注意这里是mid = (left + right + 1) // 2 ,取中位数。

def binarySearch4(nums, target):
    left, right = 0, len(nums) - 1  # 左开右闭区间 (left, right]
    while left < right:
        mid = (left + right + 1) // 2  # 注意这里取右中位数
        if nums[mid] < target:
            left = mid  # 范围缩小到 (mid, right]
        else:
            right = mid - 1  # 范围缩小到 (left, mid-1]
    return right + 1

五、总结

下面是总体代码的使用:

from typing import List


# 闭区间写法
def binarySearch1(nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1  # 闭区间 [left, right]
    while left <= right:
        # 依此循环改变俩端left, right位置直至接近target:
        # nums[left-1] < target
        # nums[right+1] >= target
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1  # 此时查找范围缩小到 [mid+1, right]
        else:
            right = mid - 1  # 此时查找范围缩小到 [left, mid-1]
    return left


# 左闭右开区间写法
def binarySearch2(nums: List[int], target: int) -> int:
    left = 0
    right = len(nums)  # 左闭右开区间 [left, right)
    while left < right:
        # nums[left-1] < target
        # nums[right] >= target
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1  # 此时查找范围缩小到 [mid+1, right)
        else:
            right = mid  # 此时查找范围缩小到 [left, mid)
    return left  # 这里返回 left 还是 right 都可以,因为循环结束后 left == right


# 开区间写法
def binarySearch3(nums: List[int], target: int) -> int:
    left, right = -1, len(nums)  # 开区间 (left, right)
    while left + 1 < right:
        # nums[left] < target
        # nums[right] >= target
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid  # 此时查找范围缩小到 (mid, right)
        else:
            right = mid  # 此时查找范围缩小到 (left, mid)
    return right


# 左开右闭区间写法
def binarySearch4(nums, target):
    left, right = 0, len(nums) - 1  # 左开右闭区间 (left, right]
    while left < right:
        mid = (left + right + 1) // 2  # 注意这里取右中位数
        if nums[mid] < target:
            left = mid  # 范围缩小到 (mid, right]
        else:
            right = mid - 1  # 范围缩小到 (left, mid-1]
    return right + 1


nums = [-99, 5, 7, 7, 8, 8, 9, 10, 11, 99]
target = 9

index = binarySearch1(nums, target)  # 选择其中一种写法即可
if index == len(nums) or nums[index] != target:
    print("不存在该target或数组为空")
else:
    print("该target下标位置未:", index)  # 输出下标: 6

最最后,假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!