面试知识库

二分查找模板:边界、答案与变形一网打尽#

二分查找看起来简单,真正上手时却最容易在边界上翻车。与其每次都临场发挥,不如直接掌握一套稳定的模板。

⚡ 速记版模板#

// 模板用途:在有序数组中查找“第一个 >= target”的位置(可用于插入位置/左边界)
int left = 0; // 左边界:当前候选区间的起点(包含)
int right = nums.length; // 右边界:当前候选区间的终点(不包含),区间定义为 [left, right)
while (left < right) { // 只要区间里还有元素,就继续二分
    int mid = left + (right - left) / 2; // 取中点,避免 (left + right) 直接相加可能溢出
    if (nums[mid] < target) { // 中点值偏小,说明目标一定在右半区
        left = mid + 1; // 丢弃 [left, mid],新区间变成 [mid + 1, right)
    } else { // 中点值大于等于 target,中点仍可能是答案
        right = mid; // 保留左半区,收缩为 [left, mid)
    }
}
return left; // 循环结束时 left == right,即第一个 >= target 的位置(插入位/左边界)
java

🎯 什么时候想到二分查找?#

当题目满足以下特征时,优先考虑二分:

  • 数据本身有序
  • 答案具有单调性
  • 需要在一个区间里快速定位某个边界
  • 题目问“最小的满足条件值”或“最大的满足条件值”

Hot 100 里的典型题目:

💡 核心不变量#

二分的关键不是背代码,而是维护好区间含义。

最常用的两种写法:

  1. 闭区间 [left, right]

    • 初始化:left = 0, right = n - 1
    • 循环条件:left <= right
    • 适合精确查找某个值
  2. 左闭右开区间 [left, right)

    • 初始化:left = 0, right = n
    • 循环条件:left < right
    • 适合找边界,更不容易写错

如果你没有强烈偏好,建议优先掌握“左闭右开”模板。

🚀 模板一:精确查找某个目标值#

适用场景:

  • 目标值是否存在
  • 找到任意一个目标下标

🚀 模板二:找左边界#

这个模板特别适合:

  • 第一个大于等于 target 的位置
  • 第一个等于 target 的位置
  • 搜索插入位置

含义:返回数组中第一个大于等于 target 的位置。

🚀 模板三:找右边界#

含义:返回数组中第一个大于 target 的位置。

如果要找最后一个等于 target 的位置,结果就是:

int rightIndex = upperBound(nums, target) - 1; // upperBound 给出第一个 > target,下标减 1 即最后一个 == target
java

🚀 模板四:答案二分#

有些题不是在数组里找数,而是在“答案空间”里找最优值。

例如:

  • 最小满足条件值
  • 最大可行值
  • 某个容量、速度、时间的最优解

这类题的关键是定义一个 check(mid):

  • 若 check(mid) 为真,说明答案可能在左边,继续收缩右边界
  • 若 check(mid) 为假,说明答案必须去右边

🧠 常见变体怎么套?#

1. 搜索插入位置#

直接套“找左边界”模板。

2. 查找某个数出现的左右边界#

  • 左边界:lowerBound(nums, target)
  • 右边界:upperBound(nums, target) - 1

3. 搜索旋转排序数组#

核心不是换模板,而是:

  • 判断哪一半有序
  • 再判断目标是否落在有序区间里

4. 寻找最小值#

利用旋转数组的结构,比较 mid 和 right 即可不断缩小区间。

⚠️ 易错点#

  1. 死循环

    • 更新区间时别写成 left = mid 或 right = mid - 1 的混搭错误
    • 先确定区间定义,再统一写法
  2. 边界越界

    • 找边界时更推荐 right = nums.length
    • 最终访问结果前先判断是否合法
  3. 中点溢出

    • 用 left + (right - left) / 2
    • 不要直接写 (left + right) / 2
  4. 左边界和右边界概念混淆

    • lowerBound 是第一个 >= target
    • upperBound 是第一个 > target

🎨 面试时怎么说#

你可以这样表达:

  1. 先说明区间定义
  2. 再说明循环条件
  3. 最后说明每次如何缩小范围

例如:

我这里维护的是左闭右开区间 [left, right),每轮用 mid 判断目标应该保留在哪一半,直到区间收缩到一个点。

📌 一句话总结#

二分查找真正要记住的不是某一段代码,而是:

  • 区间定义
  • 单调性
  • 边界收缩规则

只要这三点清楚,二分题就不会乱。