【算法】【学习笔记】二分查找

二分查找

二分查找(Binary Search),又称折半查找,是一种在有序数组中查找特定元素的高效算法。它通过不断将搜索范围缩小一半,实现 \(O(\log N)\) 的时间复杂度。

核心前提:一定要是单调的数据,才可以使用。

基本思路

以递增数据作为例子

使用双指针的方法(左指针left,右指针right),一个指首,一个指尾,来维护查询区间:

  1. 获取中间索引(mid = left + ((right - left) >> 1),这个方法避免leftright直接相加可能导致的溢出);
  2. 左闭右闭[left, right]时:( left = 0, right = n-1 )
    1. 比较中间索引的值和目标值target
      • 相等:则已经找到目标索引,跳出循环;
      • 小于目标值:说明中间值偏小,目标值在右区间,将左指针left往右收缩为mid+1
      • 大于目标值:说明中间值偏大,目标值在左区间,将右指针right往左收缩为mid-1
    2. 若左指针left > right,说明没有找到目标,返回-1
  3. 左闭右开[left, right)时:( left = 0, right = n )
    1. 比较中间索引的值和目标值target
      • 相等:则已经找到目标索引,跳出循环;
      • 小于目标值:说明中间值偏小,目标值在右区间,将左指针left往右收缩为mid+1
      • 大于目标值:说明中间值偏大,目标值在左区间,将右指针right往左收缩为mid
    2. 若左指针left >= right,说明没有找到目标,返回-1
  4. 左开右闭(left, right]时:( left = -1, right = n-1 )
    1. 比较中间索引的值和目标值target
      • 相等:则已经找到目标索引,跳出循环;
      • 小于目标值:说明中间值偏小,目标值在右区间,将左指针left往右收缩为mid
      • 大于目标值:说明中间值偏大,目标值在左区间,将右指针right往左收缩为mid-1
    2. 若左指针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

  • 二分答案:在单调的解空间中猜答案(如:求满足条件的最小值/最大值)。


小知识:高中数学就学习了二分查找的思路,笔者完全没印象!
image

posted @ 2026-07-21 21:30  SEHOD  阅读(1)  评论(0)    收藏  举报