publicintfindPeakElement(int[] nums) { intl=0, r = nums.length - 1; while (l < r) { // 注意:l < r,不是 l <= r intmid= l + (r - l) / 2; if (nums[mid] > nums[mid + 1]) r = mid; // 峰在左侧(含mid) else l = mid + 1; // 峰在右侧 } return l; // l == r 时即为峰值 }
六、在答案空间上二分(最重要的变体)
二分不只能用在下标上,任何具有单调性的答案空间都可以二分。
模板:
能否用 mid 满足条件? 能 → 缩小上界(尝试更小) 不能 → 提高下界
LeetCode 875 · 爱吃香蕉的珂珂
珂珂以速度 k 吃香蕉,问最小的 k 使得 h 小时内吃完。速度越大越容易完成,答案有单调性,直接二分速度:
publicintminEatingSpeed(int[] piles, int h) { intl=1, r = Arrays.stream(piles).max().getAsInt(); while (l < r) { intmid= l + (r - l) / 2; if (canFinish(piles, mid, h)) r = mid; // 能完成,尝试更慢 else l = mid + 1; } return l; }
booleancanFinish(int[] piles, int speed, int h) { inthours=0; for (int p : piles) hours += (p + speed - 1) / speed; return hours <= h; }
LeetCode 1011 · 在 D 天内送达包裹的能力
同样是答案二分:运载能力越大越容易在 D 天内送完,二分最小运载能力。
publicintshipWithinDays(int[] weights, int days) { intl= Arrays.stream(weights).max().getAsInt(); // 至少能装最重的包裹 intr= Arrays.stream(weights).sum(); // 最多一天送完 while (l < r) { intmid= l + (r - l) / 2; if (canShip(weights, mid, days)) r = mid; else l = mid + 1; } return l; }
booleancanShip(int[] weights, int cap, int days) { intd=1, cur = 0; for (int w : weights) { if (cur + w > cap) { d++; cur = 0; } cur += w; } return d <= days; }