- 二分查找原理
1)找≤target的最后一个数
left = 0
right = nums.length
while (left < right) {
nums[mid]==target return mid;
nums[mid]>target right = mid;
nums[mid]<target left = mid + 1
return left-1 < 0 ? -1 : left - 1;
2)找≥target的第一个数
left = 0
right = nums.length
while (left < right) {
nums[mid]==target return mid;
nums[mid]>target right = mid;
nums[mid]<target left = mid + 1
return left >= nums.length ? -1 : left;
2.利用二分查找的典型题目
1LCP 12. 小张刷题计划 - 力扣(LeetCode
基于每天完成的题目用时去计算天数
二分查找的是每天完成题目的用时
public int minTime(int[] time, int m) {
int left = 0;
int right = Integer.MAX_VALUE;
while (left < right) {
int mid = left + (right - left) / 2;
if (checkDirection(time, mid, m)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
public boolean checkDirection(int[] time, int target, int m) {
int days = 1;
int index = 0;
int sumTime = 0;
int maxTime = 0;
boolean helpler = true;
for (int i = 0; i < time.length; i++) {
sumTime += time[i];
maxTime = Math.max(maxTime, time[i]);
if (sumTime > target) {
if (helpler) {
sumTime -= maxTime;
helpler = false;
} else {
days++;
i--; //因为当前加上time[i]超过target,所以这个time[i]从当天剪掉,归为下一天
sumTime = 0;
maxTime = 0;
helpler = true;
}
}
}
return days <= m;
}
力扣练习:
2064、2187、2226
2187. 完成旅途的最少时间 - 力扣(LeetCode)
暴力解法:依次增加时间
long[] complete;
int sum = 0;
public long minimumTime1(int[] time, int totalTrips) {
complete = new long[time.length];
long timeStamp = 0;
while (sum < totalTrips) {
timeStamp++;
sum = 0;
for (int i = 0; i < time.length; i++) {
complete[i] = timeStamp / (long)time[i];
sum += complete[i];
}
}
return timeStamp;
}
----暴力解法是依次增加的时间,可以利用二分查找去寻找一个最小的timeStamp----二分查找寻找左侧边界
public long minimumTime(int[] time, int totalTrips) {
long left = 0;
long right = getRight(time, totalTrips);
while (left < right) {
long mid = left + (right - left) / 2;
if (checkYes(time, totalTrips, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
public boolean checkYes(int[] time, int totalTrips, long target) {
// 统计在target时刻所有公交车完成的总旅途数
long[] completeTrip = new long[time.length];
long sum = 0;
for (int i = 0; i < time.length; i++) {
completeTrip[i] = target / (long)time[i];
sum += completeTrip[i];
}
return sum >= totalTrips;
}
public long getRight(int[] time, int totalTrip) {
Arrays.sort(time);
// System.out.println(time[time.length - 1] * totalTrip);
return (long)time[time.length - 1] * (long)totalTrip;
}
2064. 分配给商店的最多商品的最小值 - 力扣(LeetCode)
public int minimizedMaximum(int n, int[] quantities) {
int left = 0;
int right = Integer.MAX_VALUE;
while (left < right) {
int mid = left + (right - left) / 2;
if (checkRight(n, quantities, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
public boolean checkRight(int n, int[] quantities, int target) {
// target==0,说明每个房间放0个物品,说明肯定不对,target要增加
if (target == 0) {
return false;
}
int room = 0;
int p1 = 0; // 指向的商品指针
int[] curQuan = Arrays.copyOf(quantities, quantities.length);
while (p1 < quantities.length) {
if (curQuan[p1] <= target) {
p1++;
} else {
curQuan[p1] -= target;
}
room++;
}
return room <= n;
}
2226. 每个小孩最多能分到多少糖果 - 力扣(LeetCode)
public int maximumCandies(int[] candies, long k) {
//确定查找区间
long sum = 0;
// 如果糖果数量<小孩人数,直接返回0
for (int val : candies) sum += val;
if (sum < k) return 0;
long left = 1, right = sum;
long ans = 0;
//二分答案进行查找
while (left <= right) {
long mid = left + (right - left) / 2;
if (check(candies, mid, k)) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return (int) ans;
}
//确定方法
boolean check(int[] candies, long limit, long k) {
for (int val : candies) {
if (val < limit) continue;
else if (val == limit) k--;
else {
k -= val / limit;
}
}
return k <= 0;
}