【算法】【学习笔记】二分查找
二分查找
二分查找(Binary Search),又称折半查找,是一种在有序数组中查找特定元素的高效算法。它通过不断将搜索范围缩小一半,实现 \(O(\log N)\) 的时间复杂度。
核心前提:一定要是单调的数据,才可以使用。
基本思路
以递增数据作为例子
使用双指针的方法(左指针left,右指针right),一个指首,一个指尾,来维护查询区间:
- 获取中间索引(
mid = left + ((right - left) >> 1),这个方法避免left和right直接相加可能导致的溢出); - 左闭右闭[left, right]时:(
left = 0, right = n-1)- 比较中间索引的值和目标值
target:- 相等:则已经找到目标索引,跳出循环;
- 小于目标值:说明中间值偏小,目标值在右区间,将左指针
left往右收缩为mid+1; - 大于目标值:说明中间值偏大,目标值在左区间,将右指针
right往左收缩为mid-1;
- 若左指针
left > right,说明没有找到目标,返回-1。
- 比较中间索引的值和目标值
- 左闭右开[left, right)时:(
left = 0, right = n)- 比较中间索引的值和目标值
target:- 相等:则已经找到目标索引,跳出循环;
- 小于目标值:说明中间值偏小,目标值在右区间,将左指针
left往右收缩为mid+1; - 大于目标值:说明中间值偏大,目标值在左区间,将右指针
right往左收缩为mid;
- 若左指针
left >= right,说明没有找到目标,返回-1。
- 比较中间索引的值和目标值
- 左开右闭(left, right]时:(
left = -1, right = n-1)- 比较中间索引的值和目标值
target:- 相等:则已经找到目标索引,跳出循环;
- 小于目标值:说明中间值偏小,目标值在右区间,将左指针
left往右收缩为mid; - 大于目标值:说明中间值偏大,目标值在左区间,将右指针
right往左收缩为mid-1;
- 若左指针
left >= right,说明没有找到目标,返回-1。
- 比较中间索引的值和目标值
边界处理要点
区间定义:左闭右闭、左闭右开、左开右闭会影响 mid 的更新方式。
溢出预防:使用 mid = left + ((right - left) >> 1)而非 mid = (right + left) >> 1)。
循环终止条件:left > right 说明无法找到。
代码示例
下为使用TypeScript写的左右闭合的区间,并且单调递减的例子。
function binarySearch(nums:number[], target:number){
// nums是单调递减的
const n = nums.length;
let left = 0, right = n-1, res = -1;
while(left <= right){
let mid = left + ((right - left) >> 1);
if(nums[mid] < target){
// 太小了,说明在左边大的数中
right = mid-1;
}else if(nums[mid] > target){
// 太大了,说明在右边小的数中
left = mid+1;
}else{
res = mid;
break;
}
}
return res;
}
优缺点
- 优点:可以快速处理大量数据
- 缺点:
- 一定要有序
- 数据结构需要支持 \(O(1)\) 随机访问(如数组)。
如果是单向链表,即使有序,也无法快速通过索引拿到中间值 mid,时间复杂度会退化成 \(O(N)\)。
拓展
-
查找边界:查找第一个/最后一个大于等于
target的元素位置(左/右边界)——此时要在每次满足条件时时刷新res=mid; -
二分答案:在单调的解空间中猜答案(如:求满足条件的最小值/最大值)。
小知识:高中数学就学习了二分查找的思路,笔者完全没印象!


浙公网安备 33010602011771号