目录
https://codepen.io/Vitello-Fazenbaker/pen/MYjNPQM
https://codepen.io/Vitello-Fazenbaker/pen/MYjNPQM
[❌ 错误一:mid 计算用加法可能整数溢出](#❌ 错误一:mid 计算用加法可能整数溢出)
[❌ 错误二:区间混用导致死循环](#❌ 错误二:区间混用导致死循环)
[❌ 错误三:前提条件忘记了](#❌ 错误三:前提条件忘记了)
二分查找的动画展示
https://codepen.io/Vitello-Fazenbaker/pen/MYjNPQM
一、为什么需要二分查找?
假设你要在一本 1000 页的字典里找到 "Zebra" 这个单词。
方法一:从第 1 页翻到第 1000 页,一页一页找 → 最坏要翻 1000 次。
方法二:翻到第 500 页,发现字母比 Z 小,就只在后半本找;再翻到第 750 页......每次淘汰一半,最多翻 10 次就找到了。
方法二就是二分查找的本质:每次把搜索范围缩小一半,效率是线性查找的指数级提升。
二,原理展示请打开链接(同上)
https://codepen.io/Vitello-Fazenbaker/pen/MYjNPQM
三、两种写法的对比
二分查找有两种常见写法,区别只在区间的定义方式。搞清楚这个,就不会再写出死循环了。
| 对比点 | 写法一:闭区间 [begin, end] | 写法二:左闭右开 [begin, end) |
|---|---|---|
end 初始值 |
n - 1(最后一个元素) |
n(越界一位,代表"不存在") |
while 条件 |
begin <= end(等号允许,单元素也要查) |
begin < end(等号不允许,[x,x) 是空区间) |
| 目标在左半边时 | end = mid - 1 |
end = mid(mid 本身已排除) |
| 目标在右半边时 | begin = mid + 1 |
begin = mid + 1 |
核心原则:**while 条件 和 end 的含义必须统一。**选了哪种区间定义,两处都要对应,否则必出 bug。
int BinarySearch(int* a, int n, int x) { int begin = 0; int end = n - 1; // end 指向最后一个元素 while (begin <= end) // ← 等号:单元素区间也要处理 { int mid = begin + ((end - begin) >> 1); if (x < a[mid]) end = mid - 1; // ← -1:mid 已经比较过了,排除 else if (x > a[mid]) begin = mid + 1; else return mid; } return -1; }
四、复杂度分析
时间复杂度(平均 / 最坏)
O(log n)
每次砍掉一半,n=10亿 最多也只需要 30 次比较
时间复杂度(最好)
O(1)
第一次 mid 就命中目标
空间复杂度
O(1)
只用了几个变量,不需要额外内存
五、高频易错点
❌ 错误一:mid 计算用加法可能整数溢出
不要写
mid = (begin + end) / 2,当 begin 和 end 都很大时,加法会溢出。正确写法:
mid = begin + (end - begin) / 2,或者用位运算>> 1替代除以 2(效果一样,但不要为了优化强行用,可读性更重要)。❌ 错误二:区间混用导致死循环
比如用了闭区间,while 写
begin < end(少了等号),当 begin == end 时会直接跳出,漏掉最后一个元素。❌ 错误三:前提条件忘记了
二分查找要求数组必须是有序的。如果数组无序,结果完全错误,且不会报错,是最难排查的 bug 之一。
六、面试爱考的变种
查找左边界
目标值可能重复出现,找最左边那个的下标。找到 mid==x 后不要直接返回,继续往左缩。
查找右边界
找最右边那个,找到后不返回,继续往右缩,最终返回 end。
旋转数组二分
数组被旋转过(如 [4,5,1,2,3]),每次判断哪半边是有序的,只在有序那边收缩。
实数域二分
不找下标,找满足某个条件的实数值,比如开平方根。while 条件改为
end - begin > 1e-6。答案二分
不在数据上二分,而是在"答案的值域"上二分,配合 check() 函数验证答案是否合法。
std::lower_bound
C++ STL 自带,返回第一个 >= 目标值的迭代器。面试时可以直接用,不用自己实现。
七、总结
二分查找的所有 bug,90% 来自区间定义不统一。
记住一条原则:确定区间类型(闭/左闭右开),然后 end 的初值、while 条件、end 的收缩方式,三处严格对应。
