二分法
leetcode 410、875、774、1482、1011
一、能使用二分法的题所具有的共同特点:
(1)一般不能调整数组中元素的位置
(2)一般有一个累加的过程求最值
public int minEatingSpeed(int[] piles, int H) {
int left = 1;
int right = Integer.MIN_VALUE;
for(int i=0;i<piles.length;i++){
//left = Math.min(left,piles[i]);
right = Math.max(right,piles[i]);
}
if(H == piles.length){
return right;
}
if(piles.length == 1){
return (piles[0]%H)==0 ? (piles[0]/H):(piles[0]/H+1);
}
while(left < right){
int mid = left+(right-left)/2;
if(mid == 0){
mid = 1;
}
int h = 0;
//特别注意这里h每次的取值
for(int i=0;i<piles.length;i++){
h+=(piles[i]%mid == 0)?(piles[i]/mid):(piles[i]/mid+1);
}
if(h>H){
left = mid+1;
}else{
right = mid;
}
}
return left;
}
二、本题使用了基于贪心的二分算法。注意要点:
- 首先是确定left和right的值,确定left的取值很重要,在一些题里left的值是取数组的最小值,比如说这个花束
再比如
这个题的left取值就必须要大于等于最重的包裹重量。珂珂爱吃香蕉这道题的left取值又是从1开始取。因此在确定left的取值时一定要特殊问题具体分析,从而确定left的值。right的值一般是数组中的最大值或者数组所有值的和。 - 注意在对left和right进行重新赋值时,left=mid+1,right=mid。为什么呢?
(1)先猜一个mid值,然后遍历数组划分,使每个子数组和都最接近mid(贪心地逼近mid),这样我得到的子数组数一定最少;
(2)如果即使这样子数组数量仍旧多于m个,那么明显说明我mid猜小了,因此 left = mid + 1;
(3)而如果得到的子数组数量小于等于m个,那么我可能会想,mid是不是有可能更小?值得一试。因此 right = mid;