0、参考资料(讲解视频及博客等)
- 本人水平有限,如有错误,恳请指正
- 视频教程
- 博客解析
一、应用场景
- 有序数据查询
- 在排序数组/链表中快速定位目标值(如数据库索引查询)
- 边界值探测
- 寻找第一个/最后一个满足条件的元素(如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 为例)
// 基础模板:查找目标值(数组升序)
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;
}
样例
样题:LeetCode 704. 二分查找
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) |
频繁查询但无需范围操作 |
五、相关题目推荐
- 基础应用
- 边界变种
- 抽象模型
- 进阶