Bootstrap

算法篇1:二分查找

数组篇算法一:二分查找详解

零、问题描述

给定一个 n个元素有序的(升序)整型数组 nums 和一个目标值 target,编写一个函数搜索 nums 中的 target。若目标值存在返回下标,否则返回 -1
示例
输入:nums = [-1,0,3,5,9,12], target = 9
输出:4


一、算法适用条件

  1. 有序性:数组必须按升序或降序排列(通常假设升序)。
  2. 唯一性(非必须):若数组有重复元素,需明确查找目标(如第一个/最后一个匹配项)。

二、算法步骤

1. 定义搜索区间

  • 初始区间:一般为整个数组
    • 左边界 left = 0
    • 右边界 right 可取 len(nums)-1(闭区间)或 len(nums)(左闭右开)

2. 循环缩小范围

  • 计算中间位置
    mid = left + (right - left) // 2 (避免整数溢出)
  • 比较 nums[mid]target
    • nums[mid] == target → 找到目标,返回索引
    • nums[mid] < target → 目标在右半区间,更新 left
    • nums[mid] > target → 目标在左半区间,更新 right

3. 终止条件

  • 找到目标值,直接返回
  • 若循环结束未找到,返回 -1

三、两种实现方式

方式1:闭区间写法 [left, right]

核心要点

  • right 初始为 len(nums)-1
  • 循环条件:while left <= right
  • 边界更新:right = mid - 1
def binary_search(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

方式2:左闭右开写法 [left, right)

核心要点

  • right 初始为 len(nums)
  • 循环条件:while left < right
  • 边界更新:right = mid
def binary_search(nums, target):
    left, right = 0, len(nums)
    while left < right:
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid
    return -1

总结对比

特性闭区间写法左闭右开写法
初始右边界len(nums)-1len(nums)
循环条件left <= rightleft < right
右边界更新right = mid - 1right = mid

四、变体问题

1. 查找第一个等于 target 的位置

核心逻辑:当 nums[mid] >= target 时,继续向左半区间搜索。

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

2. 查找最后一个等于 target 的位置

核心逻辑:当 nums[mid] <= target 时,继续向右半区间搜索。

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

3. 查找第一个大于等于 target 的位置

应用场景:插入位置问题(如 LeetCode 35)

def lower_bound(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] >= target:
            right = mid - 1
        else:
            left = mid + 1
    return left  # 若 target 大于所有元素,返回 len(nums)

五、经典例题

  1. LeetCode 704. 二分查找
    • 直接应用标准二分查找模板
  2. LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置
    • 结合变体问题中的第一个/最后一个位置查找
  3. LeetCode 35. 搜索插入位置
    • 等价于查找第一个大于等于 target 的位置

六、注意事项

  1. 避免死循环:确保区间更新能缩小范围(如 left = mid + 1 而非 left = mid)。
  2. 处理越界情况:返回插入位置时需检查 left 是否超出数组范围。
  3. 验证有序性:若数组未排序,需先排序(时间复杂度升至 O(n log n))。
  4. 重复元素处理:明确需求是找第一个、最后一个还是任意位置。
;