news 2026/6/20 10:35:07

再探二分查找

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
再探二分查找

各位好久不见,不知不觉2025都快要结束了,是时候来再总结一次算法(入门)的经验了。


最近笔者看标准算法库时,注意到C++算法库中只有两种二分查找的方法:lower_bound和upper_bound,分别用来查找第一个大于等于某个值的元素和第一个大于某个值的元素。

只有“第一个大于等于”和“第一个大于”,够用吗?

笔者想起了之前关于二分查找题的总结,似乎都是错题总结,而且条理很不清晰,根本没有道出二分查找使用的真相。既然标准库只提供了这俩,说明对于程序员的日常开发来讲,有这两个二分查找是完全够用的。那些之前的技巧和花招只能说是花拳绣腿,可以说都是这两种二分查找的变体。

1、查找第一个大于等于目标值的元素的位置

为了更好的理解二分查找干了什么,我想把这个过程描述为“Left指针要跳过哪些元素”,这是我们写对二分查找的关键。

从我为数不多的算法题经验来看,写二分查找的人分为两派:“Left结果派”和“Update结果派”,Left派习惯在循环结束后让left直接就是所寻找的目标值位置,而Update派习惯在二分过程中不断更新至正确答案。我是觉得Left派更好懂,而且结构清晰,有对称美。

如下,查找第一个大于等于目标值的元素。

int left = 0; int right = size()-1; //left派在left=right时跳出循环 while(right > left){ int mid = (left + right)/2; //什么时候移动left?target在mid的右边 if(target > nums[mid]){ //跳过所有的小于目标值的元素,那最终就是第一个大于等于目标值的元素 left = mid+1; //二分查找法的左指针必须要移动mid+1! } else{ right = mid; } } return left;

二分查找必须将left移动为mid+1。

如果保持left仅代替mid不移动的话,由于mid向左偏移的特性势必让算法出现死循环的情况(left在right的前一位)。而在(nums[mid]<target)条件下移动至mid+1位置,代表“left要跳过所有小于目标值的元素”,所以最终left只会落在第一个大于等于目标值的元素的位置。

除非是人为将mid的特性改为向右偏移,此时是right必须靠向left来杜绝死循环。

while(right > left){ int mid = (left + right)/2+1; //特性改为向右偏移 //什么时候移动left?target在mid的右边 if(target >= nums[mid]){ left = mid; } else{ right = mid-1; } } return left;

那这个查找到的就是最后一个小于等于目标值的元素,相当于是第一个大于等于目标值元素的左右对称算法。此时我在往期图文已有记载,但我根本记不住!这么麻烦!

但其实最后一个小于等于目标值的位置,就是“第一个大于目标值的位置 - 1)”。根本不用记那么多,只用会写开头说的那两种就好,这就是标准库的高明之处。

2、查找第一个大于目标值的元素的位置

刚刚说了二分查找的关键就是“Left指针要跳过哪些元素”,那查找第一个大于目标值的元素的位置就是“让Left指针跳过所有小于等于目标值的元素”。

如下,查找第一个大于目标值的元素。

int left = 0; int right = size()-1; //left派在left=right时跳出循环 while(right > left){ int mid = (left + right)/2; //什么时候移动left?target在mid的右边 if(target >= nums[mid]){ //跳过所有小于等于目标值的元素,那最终就是第一个大于目标值的元素 left = mid+1; } else{ right = mid; } } return left;

我们观察发现,两种查找的区别只在于Left指针跳跃的条件。仍然是保证了永远是Left指针移动至mid+1,right指针移动至mid。他们之间的不同仅仅在于要跳过的元素特性不同。在(nums[mid]<=target)条件下移动至mid+1位置,就是代表“left要跳过所有小于等于目标值的元素”,那就是第一个大于目标值的元素。

同理,当人为改变特性为向右偏移,是查找最后一个小于目标值的元素。相当于是和第一个大于目标值元素的左右对称算法。

while(right > left){ int mid = (left + right)/2+1; //什么时候移动left?target在mid的右边 if(target > nums[mid]){ left = mid; } else{ right = mid-1; } } return left;

这很麻烦,额外需要记住right往左边跳的情况,还要在二分时加上1,还要right跳到mid-1。所以我们可以直接是找到第一个大于等于目标值的元素位置后减去1,得到最后一个小于目标值的元素。

3、查找最后一个小于目标值的元素的位置

没错就是(第一个大于等于目标值的位置 - 1)。

int left = 0; int right = size()-1; //left派在left=right时跳出循环 while(right > left){ int mid = (left + right)/2; //什么时候移动left?target在mid的右边 if(target > nums[mid]){ //跳过所有的小于目标值的元素,那最终就是第一个大于等于目标值的元素 left = mid+1; //二分查找法的左指针必须要移动mid+1! } else{ right = mid; } } return left-1;

4、查找最后一个小于等于目标值的元素的位置

没错就是(第一个大于目标值的位置 - 1)。

int left = 0; int right = size()-1; //left派在left=right时跳出循环 while(right > left){ int mid = (left + right)/2; //什么时候移动left?target在mid的右边 if(target >= nums[mid]){ //跳过所有小于等于目标值的元素,那最终就是第一个大于目标值的元素 left = mid+1; } else{ right = mid; } } return left-1;

而上述的几种查找基本上可以覆盖所有常规的二分查找(默认从小到大排序)。

但是需要注意的是,上述情况均没有考虑目标值在数组范围外的情况。如果要考虑目标值在数组范围外的情况,直接用lower_bound或者upper_bound,最终left是数组的左边界或右边界。此时完全可以根据left的位置和left位置的值是否满足条件进行剪枝,可尝试LeetCode.74。

5、总结

综上所述:对于查找第一个大于等于目标值的元素和查找第一个大于目标值的元素只有一个要点——left每次必须跳到mid+1。而其余的两种变体完全可以通过这两种查找减一获得。遇到更复杂的变体只需考虑——我的left要跳过哪些元素,就都可以迎刃而解

至于两种标准库算法。

1、lower_bound

lower_bound(nums.begin(), nums.begin(), value)

返回第一个大于等于value的元素的迭代器,因为是跳过所有小于目标值的元素,写nums[mid]<target时,left=mid+1。

2、upper_bound

upper_bound(nums.begin(), nums.begin(), value)

返回第一个大于value的元素的迭代器,因为是跳过所有小于等于目标值的元素,写nums[mid]<=target时,left=mid+1。

为啥要叫这两个名字?lower_bound是第一个大于等于,比upper_bound的位置要lower一些,所以叫lower,你细品。


最近事务繁重,时常抽不出身。希望本文能给像我一样的小白一些帮助,祝福各位在2025剩下的时间里事事顺遂。

那么晚安喵~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 16:48:21

程序员的好日子真的到头了吗?2025年后端薪资大跌,相反AI相关岗位的薪资却水涨船高!

2025年的程序员招聘市场&#xff0c;正在上演一场比剧本更魔幻的现实。成都某公司的招聘启事上写着 “会调教AI写Java代码者优先” &#xff0c;深圳科技园出现了按小时结算的“程序员灵活用工中心”。 脉脉发布的《2025年度人才迁徙报告》显示&#xff0c;高薪岗位TOP20的平均…

作者头像 李华
网站建设 2026/6/17 15:49:41

基于Springboot+Vue超市仓库管理系统(完整源码+万字论文+答辩PPT)

作者贡献介绍 &#x1f497;CSDN从事毕设辅导第一人&#xff0c;本着诚信、靠谱、质量在业界获得优秀口碑&#xff0c;在此非常希望和行业内的前辈交流学习&#xff0c;欢迎成考学历咨询老师、大学老师前来合作交流&#x1f497; 2013年&#xff0c;正式踏入技术写作领域&…

作者头像 李华
网站建设 2026/6/19 22:35:44

教育软件用户体验测试:策略、挑战与最佳实践‌

教育软件的独特性与测试需求 教育软件作为数字化学习生态的核心&#xff0c;其用户体验&#xff08;UX&#xff09;直接影响学习成效和用户黏性。与传统软件不同&#xff0c;教育软件需兼顾教学性、互动性和易用性&#xff0c;例如在K-12或职业培训场景中&#xff0c;界面设计…

作者头像 李华
网站建设 2026/6/18 8:16:10

【ACWing】151. 表达式计算4

题目地址&#xff1a; https://www.acwing.com/problem/content/description/153/ 给出一个表达式,其中运算符仅包含,-,*,/,^&#xff08;加 减 乘 整除 乘方&#xff09;要求求出表达式的最终值。 数据可能会出现括号情况&#xff0c;还有可能出现多余括号情况。 数据保证不…

作者头像 李华
网站建设 2026/6/19 20:49:46

自动化测试的三种核心模式:策略选择与实践洞察

在敏捷开发与DevOps实践成为主流的当下&#xff0c;自动化测试已成为保障软件质量、加速产品迭代的关键环节。据行业报告显示&#xff0c;实施有效自动化测试的团队产品发布周期平均缩短40%。本文将深入解析基于界面的录制回放、数据驱动测试与关键字驱动测试这三种主流自动化测…

作者头像 李华