LeetCode中二分查找算法的变种
875. 爱吃香蕉的珂珂
1011. 在 D 天内送达包裹的能力
这两个题其实都是二分查找算法的变种,这两个题如果没有做过的话根本想不到他和二分查找有什么关系,其实它们内部还是隐含了一些的条件的。
首先875题,他问的是吃香蕉的速度应该怎么取,那么我们要知道吃香蕉的速度应该在什么范围之内。题目中说如果速度K大于这堆香蕉的数量的话,它这个小时吃完这堆香蕉就不会去吃第二堆的香蕉。所以我们吃香蕉的速度应该是由1开始,到这堆香蕉的最多个数的那堆的大小。
那么我们就将这个问题转化成了二分查找。查找就是在这连续升序的速度中,找到一个最小的速度并且以这个速度吃完香蕉的时间要小于等于警卫回来的时间。
/** * LC 875.这个题一眼真的看不出来是二分查找,事实上,这个珂珂吃香蕉的速度会从1到MAX(piles)中取一个值。 * 我们在这个范围之间找到最小的那一个可以吃完所有香蕉的速度就可以了。 * @param piles * @param H * @return */ public int minEatingSpeed(int[] piles, int H) { int right = -1; for(int pile:piles){ if(pile > right){ right = pile; } } int left = 1; while(left < right){ int mid = left + (right - left)/2; if(canFinish(piles,mid,H)){ right = mid; }else{ left = mid+1; } } return right; } public boolean canFinish(int[] piles,int speed, int H){ int sumTime = 0; for(int pile: piles){
//这里要向上取整,否则去尾数会导致时间变少。 sumTime += Math.ceil((1.0 * pile) / speed); } return sumTime <= H; }
然后我们再来看1011题,这个题其实也是一个二分查找的变种,我们需要在1到这堆货物的总质量中找一个最小的运载量,并且以这个速度送货物可以在D天内送完。
/** * LC 1011 * @param weights * @param D * @return */ public int shipWithinDays(int[] weights, int D) { int right = 0; for(int weight: weights){ right += weight; } int left = 0; while(left < right){ int mid = left + (right - left) / 2; if(canShip(weights, mid, D)){ right = mid; }else { left = mid + 1; } } return right; } public boolean canShip(int[] weights, int capacity, int D){ int totalDays = 0; int sum = 0; for(int i = 0; i < weights.length; i++){ sum += weights[i]; if(sum > capacity){ totalDays++; sum = weights[i]; if(sum > capacity){ return false; } } } if(sum > 0){ totalDays++; } return totalDays < D; }
这个题和上一个题的最大不同就是二分查找范围不同和判断是否能完成条件的内容不同。这里的条件比较严苛,如果目前运送的货物重量要小于等于当前运载量,我们可以继续加货物,直到超过运载量。此时我们的运送天数就要加一,运送货物总重量也得重置。
这里有个坑,如果不进行第二个if判断,可能会出现传送带上的货物重量为4,但是运载量只有3的情况。所以如果出现这种情况的话直接就返回false即可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步