【二分模版------左闭右闭】
二分查找详解与实现:左闭右闭区间(二分模板 1)
个人比较习惯的二分模版
一、引言
二分查找(Binary Search)是一个高效的搜索算法,它在有序数组中以对数时间复杂度 O(log n)
搜索目标元素的位置。二分查找非常适合用在「值单调」的问题中,如在一个升序数组中查找某个值是否存在、某个条件的最小值/最大值等。
本篇文章主要介绍一种最常用且稳定的二分查找模板:左闭右闭区间 [left, right]
的实现方法。
二、适用前提
使用二分查找有以下几个前提条件:
- 数组必须有序(升序或降序均可)
- 支持随机访问(如数组或
vector
) - 可比较目标值与当前值的大小关系
三、基本思想
在二分查找中,我们反复将搜索区间划分为两半,并根据目标值的位置来缩小搜索范围。
区间定义:左闭右闭 [left, right]
- 包括
left
和right
所指的元素,即搜索区间为a[left] ~ a[right]
。 - 因此当
left == right
时,区间仍然有效,循环条件为left <= right
。
四、为什么这样写?三点解释
---- int mid = left + (right - left) / 2
的原因?
使用 (left + right) / 2
有溢出风险,尤其在 left
和 right
很大时可能导致整型溢出,因此推荐使用:
int mid = left + (right - left) / 2;
或者位运算版本:
int mid = left + ((right - left) >> 1);
---- 为什么 while (left <= right)
?
因为我们采用的是左闭右闭区间 [left, right]
,在 left == right
时 a[left]
依然是有效元素,因此需要用 <=
。
----为什么 right = mid - 1
和 left = mid + 1
?
a[mid] > target
:说明目标在左边 → 舍弃mid
本身(因为不等于目标) →right = mid - 1
a[mid] < target
:目标在右边 → 舍弃mid
本身 →left = mid + 1
五、完整代码实现(含详尽注释)
#include <iostream>
#include <vector>
using namespace std;int main()
{// 示例有序数组(升序)vector<int> a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };// 输入要查找的目标值int target;cout << "请输入要查找的目标值: ";cin >> target;// 定义左闭右闭区间 [left, right]int left = 0;int right = a.size() - 1;// 二分查找过程while (left <= right){// 计算中间索引,防止溢出的写法int mid = left + (right - left) / 2;// 如果找到了目标if (a[mid] == target){cout << "目标值 " << target << " 的索引是: " << mid << endl;return 0;}// 如果中间值大于目标,目标在左区间else if (a[mid] > target){right = mid - 1;}// 如果中间值小于目标,目标在右区间else{left = mid + 1;}}// 若执行到此处,说明未找到目标值cout << "目标值 " << target << " 不存在于数组中" << endl;cout << "-1" << endl;return 0;
}
六、常见错误总结
错误写法 | 错误原因 |
---|---|
while (left < right) | 会漏掉最后一个元素的判断。若目标值恰好是 a[right] ,会漏查 |
mid = (left + right)/2 | 有可能溢出 |
区间定义不清晰 | 容易导致死循环或漏查,建议固定一种模板 |
七、模板通用性说明
左闭右闭区间的模板是最常用的写法,它的好处是逻辑清晰,循环条件为 left <= right
,比「左闭右开」更容易处理收敛边界情况。