二分查找二分边界查找算法的模板代码总结KeepCoding

一般而言,当一个题目出现以下特性时,你就应该立即联想到它可能需要使用二分查找:

二分查找有很多种变体,使用时需要注意查找条件,判断条件和左右边界的更新方式,三者配合不好就很容易出现死循环或者遗漏区域,本篇中我们将介绍常见的几种查找方式的模板代码,包括:

本文的内容来自于笔者个人的总结,事实上二分查找有很多种等价的写法,本文只是列出了笔者认为的最容易理解和记忆的方法。

首先给出标准二分查找的模板:

classBinarySearch{publicintsearch(int[]nums,inttarget){intleft=0;intright=nums.length-1;while(left<=right){intmid=left+((right-left)>>1);if(nums[mid]==target)returnmid;elseif(nums[mid]>target){right=mid-1;}else{left=mid+1;}}return-1;}}这里有几点需要注意:

即因为mid对于长度为偶数的区间总是偏左的,所以当区间长度小于等于2时,mid总是和left在同一侧。

Ipickanumberfrom1ton.YouhavetoguesswhichnumberIpicked.

Everytimeyouguesswrong,I'lltellyouwhetherthenumberishigherorlower.

Youcallapre-definedAPIguess(intnum)whichreturns3possibleresults(-1,1,or0):

-1:Mynumberislower1:Mynumberishigher0:Congrats!Yougotit!Example:

Input:n=10,pick=6Output:6

这题基本是可以直接照搬二分查找的,出题者没有做任何包装,我们直接使用标准二分查找:

publicclassSolutionextendsGuessGame{publicintguessNumber(intn){intleft=1;intright=n;while(left<=right){intmid=left+((right-left)>>1);if(guess(mid)==0){returnmid;}elseif(guess(mid)==-1){right=mid-1;}else{left=mid+1;}}return-1;}}实战2:Sqrt(x)Implementintsqrt(intx).Computeandreturnthesquarerootofx,wherexisguaranteedtobeanon-negativeinteger.Sincethereturntypeisaninteger,thedecimaldigitsaretruncatedandonlytheintegerpartoftheresultisreturned.这一题其实是二分查找的应用,乍一看好像和二分查找没有关系,但是我们可以用二分查找的思想快速定位到目标值的平方根,属于二分查找的一个简单运用:

classSolution{publicintmySqrt(intx){if(x<=1)returnx;intleft=1;intright=x-1;while(left<=right){intmid=left+((right-left)>>1);if(mid>x/mid){right=mid-1;}elseif(midx/(mid+1))returnmid;left=mid+1;}else{returnmid;}}return-1;//onlyforreturnavalue}}虽然是简单的题目,但是还是要注意对溢出的处理,例如我们使用mid>x/mid而不是mid*mide>x作为判断条件,因为后者可能会导致溢出,这与我们使用left+((right-left)>>1)而不是(left+right)/2作为mid的值是一个道理,这是因为left+right也可能溢出。

利用二分法寻找左边界是二分查找的一个变体,应用它的题目常常有以下几种特性之一:

类型1包括了上面说的第一种,第二种情况。

既然要寻找左边界,搜索范围就需要从右边开始,不断往左边收缩,也就是说即使我们找到了nums[mid]==target,这个mid的位置也不一定就是最左侧的那个边界,我们还是要向左侧查找,所以我们在nums[mid]偏大或者nums[mid]就等于目标值的时候,继续收缩右边界,算法模板如下:

classSolution{publicintsearch(int[]nums,inttarget){intleft=0;intright=nums.length-1;while(left

首先,这里的右边界的更新是right=mid,因为我们需要在找到目标值后,继续向左寻找左边界。

其次,这里的循环条件是left

事实上,我们只需要遍历到left和right相邻的情况就行了,因为这一轮循环后,无论怎样,left,mid,right都会指向同一个位置,而如果这个位置的值等于目标值,则它就一定是最左侧的目标值;如果不等于目标值,则说明没有找到目标值,这也就是为什么返回值是nums[left]==targetleft:-1。

左边界查找的第二种类型用于数组部分有序且包含重复元素的情况,这种条件下在我们向左收缩的时候,不能简单的令right=mid,因为有重复元素的存在,这会导致我们有可能遗漏掉一部分区域,此时向左收缩只能采用比较保守的方式,代码模板如下:

classSolution{publicintsearch(int[]nums,inttarget){intleft=0;intright=nums.length-1;while(lefttarget){right=mid;}else{right--;}}returnnums[left]==targetleft:-1;}}它与类型1的唯一区别就在于对右侧值的收缩更加保守。这种收缩方式可以有效地防止我们一下子跳过了目标边界从而导致了搜索区域的遗漏。

关于这种类型的例子,可以看下面的实战5。

这道题的题目比较长,原题就不贴了,大意就是说:有这么一个数组:

[false,false,false,...,fasle,true,true,...,true]求最左侧的那个true的位置。

这就是一个典型的查找左边界的问题:数组中包含重复元素,我们需要找到最左侧边界的位置。直接使用二分查找左边界的模板就行了:

(i.e.,[0,1,2,4,5,6,7]mightbecome[4,5,6,7,0,1,2]).

Findtheminimumelement.

Youmayassumenoduplicateexistsinthearray.

这一题看上去没有重复元素,但是它也是查找左边界的一种形式,即可以看做是查找旋转到右侧的部分的左边界,有了这个思想,直接用二分查找左边界的模板就行了:

classSolution{publicintfindMin(int[]nums){if(nums.length==1)returnnums[0];intleft=0;intright=nums.length-1;while(left>1);if(nums[mid]>nums[nums.length-1]){left=mid+1;}else{right=mid;}}returnnums[left];}}实战5:FindMinimuminRotatedSortedArrayII这道题目和上面的实战2类似,只是多了一个条件——数组中可能包含重复元素,这就是我们之前说的二分查找左边界的第二种类型,在这种情况下,我们只能采用保守收缩的方式,以规避重复元素带来的对于单调性的破坏:

classSolution{publicintfindMin(int[]nums){if(nums.length==1)returnnums[0];intleft=0;intright=nums.length-1;while(left>1);if(nums[mid]>nums[right]){//mid位于旋转点左侧left=mid+1;}elseif(nums[mid]

classSolution{publicintsearch(int[]nums,inttarget){intleft=0;intright=nums.length-1;while(left>1)+1;if(nums[mid]>target){right=mid-1;}else{left=mid;}}returnnums[right]==targetright:-1;}}这里大部分和寻找左边界是对称着来写的,唯独有一点需要尤其注意——中间位置的计算变了,我们在末尾多加了1。这样,无论对于奇数还是偶数,这个中间的位置都是偏右的。

对于这个操作的理解,从对称的角度看,寻找左边界的时候,中间位置是偏左的,那寻找右边界的时候,中间位置就应该偏右呗,但是这显然不是根本原因。根本原因是,在最后left和right相邻时,如果mid偏左,则left,mid指向同一个位置,right指向它们的下一个位置,在nums[left]已经等于目标值的情况下,这三个位置的值都不会更新,从而进入了死循环。所以我们应该让mid偏右,这样left就能向右移动。这也就是为什么我们之前一直强调查找条件,判断条件和左右边界的更新方式三者之间需要配合使用。

右边界的查找一般来说不会单独使用,如有需要,一般是需要同时查找左右边界。

前面我们介绍了左边界和右边界的查找,那么查找左右边界就容易很多了——只要分别查找左边界和右边界就行了。

这是一道特别标准的查找左右边界的题目,我们只需要分别查找左边界和右边界就行了:

classSolution{publicint[]searchRange(int[]nums,inttarget){int[]res=newint[]{-1,-1};if(nums==null||nums.length==0)returnres;//findtheleft-endintleft=0;intright=nums.length-1;while(left>1);if(nums[mid]>1)+1;if(nums[mid]>target){right=mid-1;}else{left=mid;}}res[1]=right;}}returnres;}}二分查找极值二分查找还有一种有趣的变体是二分查找极值点,之前我们使用nums[mid]去比较的时候,常常是和给定的目标值target比,或者和左右边界比较,在二分查找极值点的应用中,我们是和相邻元素去比,以完成某种单调性的检测。关于这一点,我们直接来看一个例子就明白了。

这一题的有趣之处在于他要求求一个局部极大值点,并且整个数组不包含重复元素。所以整个数组甚至可以是无序的——你可能很难想象我们可以在一个无序的数组中直接使用二分查找,但是没错!我们确实可以这么干!谁要人家只要一个局部极大值即可呢。

classSolution{publicintfindPeakElement(int[]nums){intleft=0;intright=nums.length-1;while(left>1);if(nums[mid]

除了本文所介绍的二分查找的应用方式,二分查找其实还有很多其他的变体和应用,但它们基本上是循环条件,判断条件,边界更新方法的不同组合,例如,有的二分查找的循环条件可以是while(left+1

但是无论如何,代码模板只是给大家一个理解问题的角度,生搬硬套总是不好的。实际应用中,我们只要记住循环条件,判断条件与边界更新方法三者之间的配套使用就行了,基于这一点原则,你就可以使用你自己习惯的方式来实现二分搜索。

但是,如果你真的只是想应付面试,我想下面这个表的总结应该就差不多足够用了:

最后,希望大家在理解二分查找的思想后都能够写出适合自己的搭配方式,共勉!

THE END
1.寻找论文中的代码知道算法公式怎么找代码邮件联系第一作者(不限于第一作者).如果是一些博士生,有的还是乐于分享代码的,这样自己的文章也更容易被同行引用。 查看引用该论文且使用该论文作为baseline或比较对象的其他论文,找这些论文的作者要代码。 某些论文算法可以分步解决,则可以分别找每一步的代码。 https://blog.csdn.net/qq_42235129/article/details/102814312
2.怎么查看python算法库中算法的源代码在数据科学和机器学习的领域,Python算法库如scikit-learn、numpy和pandas等,功能强大且广泛应用,但了解它们底层实现的细节通常被忽略。因此,设计一个方案以便查看这些算法库中算法的源代码将对学习和研究有很大帮助。 项目目标 教会用户如何获取Python算法库的代码。 https://blog.51cto.com/u_16175436/12335221
3.c语言源代码怎么找C++c语言源代码怎么找 您可以通过以下方式查找 c 语言源代码:查看开源代码库(如 github、bitbucket 和 sourceforge);访问特定领域网站(如 leetcode、hackerrank 和 codechef);利用本地资源(如软件包管理系统);查找现成的项目(如第三方 c 语言库);使用搜索引擎(如 google 或 bing);使用提示(如许可证筛选和代码健康https://www.php.cn/faq/843998.html
4.如何获取代码60秒读懂世界全面指南:如何轻松获取代码及高效使用 引言: 在当今数字化时代,代码已成为推动技术发展的重要工具。无论是开发者还是对编程感兴趣的学习者,获取代码都是开展项目、学习新技术的基础。本文将为您详细解析如何获取代码,以及如何高效地使用这些代码资源。 一、代码获取途径 https://blog.yyzq.team/post/468230.html
5.Python中的查找算法代码实例python这篇文章主要介绍了Python中的查找算法代码实例,算法是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出,简单来说,算法就是解决一个问题的具体方法和步骤,算法是程序的灵魂,需要的朋友可以参考下https://www.jb51.net/python/293423gps.htm
6.哈希查找算法的源代码c语言哈希查找算法的源代码c语言【问题描述】针对自己的班集体中的“人名”设计一个哈希表,使得平均查找长度不超过R,完成相应的建表和查表程序。[基本要求]假设人名为中国姓名**语拼音形式。待填入哈希表的人名共有30个,取平均查找长度的上限为2。哈希函数用除留余数法构照,用链表法处理冲突。[测试数据]读取熟悉的30https://www.docin.com/p-646749586.html
7.那些经典的算法:从猜数字到二分查找算法二分查找算法代码实现 publicclassTestBinSearch{staticintbsearch(inta[],intsize,intsearchValue){intlow=0;inthigh=size-1;//用high-low 是为了防止数组过大,两数相加溢出,用移位是为了提升性能intmid=(high-low)>>1+low;while(low<=high){if(a[mid]>searchValue){high=mid-1;}elseif(a[mid]<searchhttps://www.jianshu.com/p/337a81db5e28
8.科学网—[转载]Delaunay三角剖分及算法基本知识2.计算Delaunay三角剖分的算法及分析 3.例子程序&代码 大话 点集的三角剖分(Triangulation),对数值分析(比如有限元分析)以及图形学来说,都是极为重要的一项预处理技术。 尤其是Delaunay三角剖分,由于其独特性,关于点集的很多种几何图都和Delaunay三角剖分相关,如Voronoi图,EMST树,Gabriel图等。Delaunay三角剖分有几https://blog.sciencenet.cn/blog-116465-216935.html
9.实验指导数据结构教学运行与管理三、实验源代码 四、实验结果(测试数据) 第六章 递归实验 6.1折半查找递归算法 一、实验目的 1.理解递归调用的实现过程 2.学会递归程序的设计方法 二、实验内容 1.编写折半查找的递归程序。 2.在VC++的调试环境下观察折半查找递归程序的调用与返回过程,并记录其过程和返回值。 https://www.gxtcmu.edu.cn/ggxy/jysjs1/xxglyxxxtjysyxxxgcjyshs/jxyhygl2/sjjg/content_29239
10.自动驾驶路径规划技术A*启发式搜索算法腾讯云开发者社区A*算法是一种大规模静态路网中求解最短路径最有效的搜索方法,相比于Dijkstra算法,它提供了搜索方向的启发性指引信息,在大多数情况下大大降低了Dijkstra算法无效的冗余的扩展搜索,因此也成为自动驾驶路径规划中的首选算法。 Dijkstra算法和A*算法的伪代码如下,可以看到A*算法搜索过程中,增加了对于未来预测的启发性的Costhttps://cloud.tencent.com/developer/article/1989495
11.GitHubCoding4Real/leetcodePDF版本:「代码随想录」算法精讲 PDF 版本。 最强八股文::代码随想录知识星球精华PDF 刷题顺序: README已经将刷题顺序排好了,按照顺序一道一道刷就可以。 学习社区: 一起学习打卡/面试技巧/如何选择offer/大厂内推/职场规则/简历修改/技术分享/程序人生。欢迎加入「代码随想录」知识星球。 https://github.com/Coding4Real/leetcode-master
12.腾讯算法岗武功秘籍(上)所以,不要存在侥幸心理,踏踏实实的刷题,复习好常规机器学习算法,尤其是算法的原理和应用场景。 ★ 项目和比赛经历非常的重要,往往面试官都是根据项目里用到的方法拓展提问,对项目的优化和改进也问的比较多。还有就是能内推的一定去找学长学姐或是其它资源去内推。 ★ 面试过程中如果实在写不出来代码的话,就给https://www.flyai.com/article/930
13.信息学奥赛算法专题:三分查找搜索算法的步骤及代码以上思路参考了《三分查找》,但也对代码按照我自己的理解,进行了修改,也方便给学生解释。04 —另外一种取值方法 lmid与rmid的取值:一般可以将这两个点取为[l,r]的三等分点。即 lmid=l+(r-l)/3.0;rmid=r-(l-r)/3.0;信息学本身是一门比较难的学科,很多学生会因为老师讲的不够详细,不够透彻,https://baijiahao.baidu.com/s?id=1768637125397387649&wfr=spider&for=pc
14.速石科技Fsched:国产自研调度器的璀璨新星,数百DEBUG:深入代码级的技术支持 举一个典型例子:当研发提交任务出现异常状态,怎么办? 我们首先需要定位与任务相关的日志。日志分为:基础设施层日志、中间件层日志、应用层日志等。 IT和研发工程师的关注点不一样:IT工程师一般看基础设施层日志,CAD和研发工程师看中间件层日志和应用层日志。不同角色各看各的,定位问题https://www.cet.com.cn/itpd/itxw/3439124.shtml