// 时间复杂度 O(N) booleancanFinish(int[] piles, int speed, int H){ int time = 0; for (int n : piles) { time += timeOf(n, speed); } return time <= H; } inttimeOf(int n, int speed){ return (n / speed) + ((n % speed > 0) ? 1 : 0); } intgetMax(int[] piles){ int max = 0; for (int n : piles) max = Math.max(n, max); return max; }
至此,借助二分查找技巧,算法的时间复杂度为 O(NlogN)。
二、扩展延伸
类似的,再看一道运输问题: 要在 D 天内运输完所有货物,货物不可分割,如何确定运输的最小载重呢(下文称为 cap)? 其实本质上和 Koko 吃香蕉的问题一样的,首先确定 cap 的最小值和最大值分别为 max(weights) 和 sum(weights)。 我们要求最小载重,所以可以用搜索左侧边界的二分查找算法优化线性搜索:
// 寻找左侧边界的二分查找 intshipWithinDays(int[] weights, int D){ // 载重可能的最小值 int left = getMax(weights); // 载重可能的最大值 + 1 int right = getSum(weights) + 1; while (left < right) { int mid = left + (right - left) / 2; if (canFinish(weights, D, mid)) { right = mid; } else { left = mid + 1; } } return left; } // 如果载重为 cap,是否能在 D 天内运完货物? booleancanFinish(int[] w, int D, int cap){ int i = 0; for (int day = 0; day < D; day++) { int maxCap = cap; while ((maxCap -= w[i]) >= 0) { i++; if (i == w.length) returntrue; } } returnfalse; }
通过这两个例子,你是否明白了二分查找在实际问题中的应用?
1 2 3
for (int i = 0; i < n; i++) if (isOK(i)) return ans;