上一篇讲了二分查找的基础知识和细节的处理。元素等于目标值的三种情况。这些情况都适用于在有序数组中查找指定元素的基本场景,但在实际应用中可能就没那么直接了。就算看了题目,也绝对不会想到可以用二分查找算法来求解。本文分析二分查找在实践中的应用。通过分析几个二分查找的应用实例,总结出一些可以使用二分查找算法的共同点。以后遇到相关的实际问题时,可以有一个基本的分析方法,做到毫无头绪。基本的二分查找首先,让我们回顾一下二分查找的基本框架。总的来说,实际场景是寻找与目标相等的最左元素或最右元素。代码如下:求左边界intbinary_search_firstequal(vector&vec,inttarget){intilen=(int)vec.size();如果(ilen<=0)返回-1;int左=0;intright=ilen-1;while(left<=right){intmid=left+(right-left)/2;//找到目标,继续向左找目标if(target==vec[mid])right=mid-1;elseif(target&vec,inttarget){intilen=(int)vec.size();如果(ilen<=0)返回-1;int左=0;intright=ilen-1;while(left<=right){intmid=left+(right-left)/2;//找到目标,继续向右找目标if(target==vec[mid])left=mid+1;elseif(targetvec;//数组intleft=0;//数组起始索引intright=ilen-1;//数组的结束索引while(left<=right)//在数组中找到目标的索引{intmid=left+(right-left)/2;if(target==vec[mid])returnmid;}上面二分查找的单调关系是什么?它是数组的索引和索引处元素的值。索引越大,元素的值就越大。伪代码表示如下:intfunc(vector&vec,intindex){returnvec[index];}intsearch(vector&vec,inttarget){while(left<=right){intmid=左+(右-左)/2;if(target==func(vec,mid)){....}elseif(target>func(vec,mid)){...}else{...}}}在上面的伪代码中,我们使用a函数func用于表示传入的参数是一个数组和数组的索引,函数返回数组指定索引处的元素。在二分查找的while循环中,直接将target与func函数的返回值进行比较。听起来有点抽象,我们直接从leetcode分析几题。例1:爱吃香蕉的科科,从题目的描述来看和有序数组没有任何关系,所以刚开始看这道题的时候想不到用二分查找的方式来分析。如果看完题目没有什么想法,可以看看题目中涉及的条件,看看能不能分析出一些有用的点。问题分析咳咳想吃香蕉。他面前有N堆。咳咳一小时可以吃完K根香蕉,但是如果一堆不到K根,就要一个小时了。如果一堆大于K根,则超过K的部分也算1小时。根据题意可知:咳咳吃香蕉越慢,耗时越长。反之,速度越大,耗时越少,这就是题目的单调关系。我们要找的是速度,因为问题限制了客客一个小时内只能选择一串香蕉吃,所以速度最大的就是这一堆香蕉中数量最多的那个,速度最小的无疑是1是的,可以通过轮询数组来获得最大速度。intmaxspeed=1;for(auto&elem:vec){if(elem>maxspeed)maxspeed=elem;}+因为可可在一个小时内只能选择一串香蕉吃,所以,每串香蕉的消耗就是时间=这堆香蕉的数量/客客一小时吃的香蕉数量。根据题意,这里的/不可整除时,吃完剩下的需要1小时,所以吃完一串香蕉的时间可以表示为。小时=堆[i]/速度;if(0!=piles[i]%speed)小时+=1;确定香蕉的堆数和每堆的数量,输入在H小时内吃完的时间参数也确定。现在唯一不确定的是吃香蕉的速度。我们需要做的是在最小速度和最大速度之间找到一个速度,使得香蕉可以在H小时内吃完。如前所述,吃香蕉的速度与吃完香蕉所需的时间之间存在单调关系。我们先来定义单调关系的函数。//当速度为speed时,吃完所有一堆食物需要多少小时inteatingHour(vector&piles,intspeed){if(speed<=0)return-1;int小时=0;for(auto&iter:piles){hour+=iter/speed;if(0!=iter%speed)小时+=1;}returnhour;}题目要求吃完香蕉后的最小速度,也就是速度要足够小,小到刚好在H小时内所有的香蕉都吃完了,所以是左边界速度。好了,分析完了,写代码:intminEatingSpeed(vector&piles,inth){//一个小时最多能吃多少根香蕉intmaxcount=1;for(auto&iter:piles){if(maxcount&weights,intcapacity){//船舶载重检查if(capacity<=0)return-1;intisize=(int)weights.size();内部温度=0;int天=0;for(inti=0;i0)++days;returndays;}hiddeninthetitle包含几个信息:船的最小载重需要大于等于传送带上最重包裹的重量,因为一次至少要装载一个包裹。船的最大载重等于传送带上所有包裹的总重量,即一次可以装载所有包裹。这艘船每天只能运送一个包裹。确定了船舶的运载范围之后,就相当于确定了二分查找的区间。另外,题目是求船的最小载重量,相当于求载重量的左边界。分析到这里,就可以写出基本的搜索框架了,这里直接上代码。intshipWithinDays(vector&weights,intdays){intisize=(int)weights.size();如果(isize<=0)返回0;//最小负载必须等于货物的最大重量intmincapacity=0;//最大负载,所有货物重量的总和intmaxcapacity=0;for(auto&iter:weights){最大容量+=iter;如果(iter>mincapacity){mincapacity=iter;}}intl=mincapacity;intr=最大容量;while(l