二分查找算法详解:从理论到实践
二分查找虽然概念简单,但实现时的细节需要进行思考,不要想当然。正确处理循环条件、中间位置计算和指针更新是写出无bug代码的关键。
问题描述
LeetCode 704题要求在一个有序数组中查找目标值,返回其索引位置。这是一个典型的二分查找问题,要求时间复杂度为O(log n)。
核心思路
二分查找的基本思想是通过将搜索区间反复折半来定位目标值。但在实际编码中,有几个关键点需要特别注意:
1. 循环条件的设置
while (end >= start)
为什么使用 >=
而不是 >
?
- 使用
>
会漏掉最后一个元素的情况 - 当 start = end 时,这个位置的元素仍然需要被检查
- 例如:在数组 [1] 中查找 1,如果使用
>
,则永远无法检查到这个元素
2. 中间位置的计算
int mid = start + (end - start) / 2;
为什么不直接写 (start + end) / 2
?
- 避免整数溢出
- 当 start 和 end 都很大时,它们的和可能超出 int 的范围
- 使用
start + (end - start) / 2
可以确保计算过程中不会溢出 - 如果仅使用
end = mid
或start = mid
,会导致死循环 - 因为如果目标值不存在,mid 可能永远不变
- 通过
mid ± 1
,确保每次都在缩小搜索范围
3. 指针更新策略
if (nums[mid] > target)
end = mid - 1;
else
start = mid + 1;
为什么要 mid ± 1
?
- 如果仅使用
end = mid
或start = mid
,会导致死循环 - 因为如果目标值不存在,mid 可能永远不变
- 通过
mid ± 1
,确保每次都在缩小搜索范围
Implementation:
class Solution {
public:
int search(vector<int>& nums, int target) {
int start =0, end = nums.size() - 1;
while (end >= start) {
int mid = start + (end - start) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] > target) end = mid - 1;
else start = mid + 1;
}
return -1;
}
};