class Solution:
def solve(self, piles, k):
def possible(mid):
t = 0
for pile in piles:
t += (pile + mid - 1) // mid
return t <= k
l, r = 1, max(piles)
while l <= r:
mid = (l + r) // 2
if possible(mid):
r = mid - 1
else:
l = mid + 1
return l
JavaScript Code:
function canEatAllBananas(piles, H, mid) {
let h = 0;
for (let pile of piles) {
h += Math.ceil(pile / mid);
}
return h <= H;
}
/**
* @param {number[]} piles
* @param {number} H
* @return {number}
*/
var minEatingSpeed = function (piles, H) {
let lo = 1,
hi = Math.max(...piles);
// [l, r) , 左闭右开的好处是如果能找到,那么返回 l 和 r 都是一样的,因为最终 l 等于 r。
while (lo <= hi) {
let mid = lo + ((hi - lo) >> 1);
if (canEatAllBananas(piles, H, mid)) {
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return lo; // 不能选择hi
};
复杂度分析
时间复杂度:$O(max(N, N * logM))$,其中 N 为 piles 长度, M 为 Piles 中最大的数。
空间复杂度:$O(1)$
模板
分享几个常用的的二分法模板。
查找一个数
public int binarySearch(int[] nums, int target) {
// 左右都闭合的区间 [l, r]
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
// 搜索区间变为 [mid+1, right]
left = mid + 1;
else if (nums[mid] > target)
// 搜索区间变为 [left, mid - 1]
right = mid - 1;
}
return -1;
}
寻找最左边的满足条件的值
public int binarySearchLeft(int[] nums, int target) {
// 搜索区间为 [left, right]
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
// 搜索区间变为 [mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 搜索区间变为 [left, mid-1]
right = mid - 1;
} else if (nums[mid] == target) {
// 收缩右边界
right = mid - 1;
}
}
// 检查是否越界
if (left >= nums.length || nums[left] != target)
return -1;
return left;
}
寻找最右边的满足条件的值
public int binarySearchRight(int[] nums, int target) {
// 搜索区间为 [left, right]
int left = 0
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
// 搜索区间变为 [mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 搜索区间变为 [left, mid-1]
right = mid - 1;
} else if (nums[mid] == target) {
// 收缩左边界
left = mid + 1;
}
}
// 检查是否越界
if (right < 0 || nums[right] != target)
return -1;
return right;
}