二分查找(含有动画展示):不再写出死循环

目录

二分查找的动画展示

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 的收缩方式,三处严格对应。

相关推荐
吃饱了得干活7 小时前
Spring Cloud Gateway 微服务网关:路由、断言、过滤器
java·spring cloud
lwx572809 小时前
探秘InnoDB:搞懂它的内存、线程、磁盘与日志刷盘策略
java·后端
Flynt10 小时前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
plainGeekDev11 小时前
Activity 间传值 → Navigation 参数
android·java·kotlin
plainGeekDev11 小时前
onActivityResult → ActivityResult API
android·java·kotlin
Sunia11 小时前
《AgentX 专栏》10-生产部署:3台2C4G云服务器把企业级Agent真正跑起来的完整方案
java·架构
ZhengEnCi12 小时前
J7A-高级Java工程师面试三道灵魂拷问-深度广度与工程素养的终极检验
java·后端
狼爷1 天前
吃透 Java Function 接口,搞定 99% 的 Stream 场景
java·函数式编程
祎雪双十Gy1 天前
从 DataX 的配置加载说起:我用 FastJson2 做了一个轻量级动态配置管理库
java·后端
小锋java12341 天前
分享一套锋哥原创的SpringBoot4+Vue3宠物领养网站系统
java