0、参考资料(讲解视频及博客等)
- 视频教程
- 博客解析
- 知乎高赞:二分法的11种变种场景与避坑指南
- 《代码随想录》二分法专题(https://programmercarl.com/0704. 二分查找.html)
一、应用场景
- 有序 数据查询
- 在排序数组/链表中快速定位目标值(如数据库索引查询)
- 边界值探测
- 寻找第一个/最后一个满足条件的元素(如IP地址归属地匹配)
- 数值计算优化
- 求平方根、对数近似值(可结合牛顿迭代法)
- 单调函数极值
- 寻找峰值、谷值(如股票最佳买卖时机)
- 答案范围限定问题
- 在可能解区间内二分逼近最优解(如分蛋糕问题、Koko吃香蕉问题)
二、核心思想/步骤
-
核心思想
- 区间折半:通过不断缩小候选区间,将线性搜索优化为对数级
- 循环不变量 :保持区间定义的一致性(左闭右闭
[left, right]或左闭右开[left, right))
-
通用步骤
a. 初始化左右边界(需覆盖所有可能解)
b. while(left ≤ right):
i. 计算 mid = left + (right - left)/2 // 防溢出
ii. 根据 mid 值与目标关系,选择左半或右半区间
c. 返回最终索引或边界值 -
关键细节
- 终止条件 :
left > right时结束循环 - 边界更新 :
- 找精确值:
left = mid + 1或right = mid - 1 - 找左边界:
right = mid(左闭右开) - 找右边界:
left = mid + 1
- 找精确值:
- 终止条件 :
三、代码模板与测试
代码模板 (java 为例)
java
// 基础模板:查找目标值(数组升序)
public int binarySearch(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
// 变种模板:寻找左边界(如LeetCode 34)
public int findLeftBound(int[] nums, int target) {
int left = 0, right = nums.length; // 右开区间
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) right = mid;
else left = mid + 1;
}
return (left < nums.length && nums[left] == target) ? left : -1;
}
样例
class Solution {
public int search(int[] nums, int target) {
int l = 0,r = nums.length-1;
while(l<=r){
int mid = (l+r)>>1;
if(nums[mid]==target){
return mid;
}
else if(nums[mid]<target){
l = mid + 1;
}
else{
r = mid - 1;
}
}
return -1;
}
}
四、优化与同类算法对比
优化方案
- 计算优化
- 用位运算代替除法:
mid = (left + right) >> 1(Java无符号右移)
- 用位运算代替除法:
- 区间定义统一
- 始终使用左闭右开区间,减少边界条件判断
- 提前终止
- 在数据量极大时,添加剪枝逻辑,提前结束不可能产生预期解的区间
算法对比/复杂度分析
| 算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 二分法 | O(log n) | O(1) | 有序数据或隐式单调函数 |
| 线性扫描 | O(n) | O(1) | 无序小规模数据 |
| 哈希表 | O(1) | O(n) | 频繁查询但无需范围操作 |
五、相关题目推荐
- 基础应用
- 边界变种
- 抽象模型
- 进阶