二分搜索问题泛化
什么问题可以用二分搜索?所有可以抽象出如下信息的题目都可以采用二分搜索:
- 抽象出一个自变量x,一个关于x的单调函数f(x)
- 题目需要求f(x)==target时x的最小值或者最大值
410. 分割数组的最大值
题目:给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
分析:x表示子数组和的最大值,f(x)表示最大值为x的情况下子数组的个数(单调递减),求最小值
代码:
class Solution {
public int splitArray(int[] nums, int m) {
int max = 0;
int sum = 0;
for (int num : nums){
max = Math.max(max, num);
sum += num;
}
int left = max;
int right = sum;
while (left <= right){
int mid = left + (right - left)/2;
int splits = split(nums, mid);
if (splits > m){
left = mid + 1;
}else if (splits < m){
right = mid - 1;
}else if (splits == m){
right = mid - 1;
}
}
return left;
}
public int split(int[] nums, int maxSum){
int splits = 1;
int curSum = 0;
for (int num : nums){
if (curSum + num > maxSum){
curSum = 0;
splits++;
}
curSum += num;
}
return splits;
}
}
875. 爱吃香蕉的珂珂
题目:珂珂喜欢吃香蕉。这里有n堆香蕉,第i堆中有piles[i]根香蕉。警卫已经离开了,将在h小时后回来。珂珂可以决定她吃香蕉的速度k(单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉k根。如果这堆香蕉少于k根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。返回她可以在h小时内吃掉所有香蕉的最小速度k(k为整数)。
分析:x表示吃香蕉的速度,f(x)表示吃完需要的时间(单调递减),求最小值
代码:
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int max = 0;
for (int pile : piles){
max = Math.max(max, pile);
}
int left = 1;
int right = max;
while (left < right){
int mid = left + (right - left)/2;
int count = calculateSum(piles, mid);
if (count < h){
right = mid;
}else if (count > h){
left = mid + 1;
}else{
right = mid;
}
}
return left;
}
public int calculateSum(int[] nums, int speed){
int time = 0;
for (int num : nums){
if (num % speed == 0){
time += num/speed;
}else{
time += num/speed + 1;
}
}
return time;
}
}
LCP 12. 小张刷题计划
题目:为了提高自己的代码能力,小张制定了 LeetCode 刷题计划,他选中了 LeetCode 题库中的 n 道题,编号从 0 到 n-1,并计划在 m 天内按照题目编号顺序刷完所有的题目(注意,小张不能用多天完成同一题)。在小张刷题计划中,小张需要用 time[i] 的时间完成编号 i 的题目。此外,小张还可以使用场外求助功能,通过询问他的好朋友小杨题目的解法,可以省去该题的做题时间。为了防止“小张刷题计划”变成“小杨刷题计划”,小张每天最多使用一次求助。我们定义 m 天中做题时间最多的一天耗时为 T(小杨完成的题目不计入做题总时间)。请你帮小张求出最小的 T是多少。
分析:x表示每天刷题的时间,f(x)刷完需要的天数(单调递减),求最小值
代码:
class Solution {
public int minTime(int[] time, int m) {
int left = 0;
int right = Integer.MAX_VALUE;
while (left <= right){
int mid = left + (right - left)/2;
int day = calcuTime(time, mid);
if (day <= m){
right = mid - 1;
}else{
left = mid + 1;
}
}
return left;
}
public int calcuTime(int[] time, int maxTime){
int day = 1;
boolean help = true;
int curTime = 0;
int max = 0;
for (int i = 0; i < time.length; i++){
max = Math.max(max, time[i]);
curTime += time[i];
if (curTime > maxTime){
if (help){
curTime -= max;
help = false;
}else{
day++;
help = true;
max = 0;
curTime = 0;
i--;
}
}
}
return day;
}
}
1011. 在 D 天内送达包裹的能力
题目:传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量(weights)的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。返回能在 days 天内将传送带上的所有包裹送达的船的最低运载能力。
分析:x表示每天的运载量,f(x)表示运完需要的天数(单调递减),求最小值
代码:
class Solution {
public int shipWithinDays(int[] weights, int days) {
int max = 0;
int sum = 0;
for (int weight : weights){
max = Math.max(max, weight);
sum += weight;
}
int left = max;
int right = sum;
while (left <= right){
int mid = left + (right - left)/2;
int num = calcuDay(weights, mid);
if (num < days){
right = mid - 1;
}else if (num > days){
left = mid + 1;
}else{
right = mid - 1;
}
}
return left;
}
public int calcuDay(int[] weights, int maxNum){
int num = 1;
int curNum = 0;
for (int weight : weights){
if (curNum + weight > maxNum){
num++;
curNum = 0;
}
curNum += weight;
}
return num;
}
}
1482. 制作 m 束花所需的最少天数
题目:给你一个整数数组 bloomDay,以及两个整数 m 和 k 。现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好可以用于一束花中。请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。
分析:x表示限制花开的天数,f(x)表示在x天的条件下能够制作的花束的数量(单调递增),求最小值
代码:
class Solution {
public int minDays(int[] bloomDay, int m, int k) {
if (bloomDay.length < m * k){
return -1;
}
int min = 0;
int max = 0;
for (int b : bloomDay){
min = Math.min(min, b);
max = Math.max(max, b);
}
int left = min;
int right = max;
while (left < right){
int mid = left + (right - left)/2;
int count = calcuFlower(bloomDay, mid, m, k);
if (count >= m){
right = mid;
}else{
left = mid + 1;
}
}
return left;
}
public int calcuFlower(int[] days, int maxDay, int m, int k){
int flower = 0;
int num = 0;
for (int day : days){
if (day > maxDay){
flower = 0;
}else{
flower++;
if (flower == k){
num++;
flower = 0;
}
}
}
return num;
}
}
1552. 两球之间的磁力
题目:在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n 个空的篮子,第 i 个篮子的位置在 position[i] ,Morty 想把 m 个球放到这些篮子里,使得任意两球间 最小磁力最大。已知两个球如果分别位于 x 和 y ,那么它们之间的磁力为 |x - y| 。给你一个整数数组 position 和一个整数 m ,请你返回最大化的最小磁力。
分析:x表示两球间的最小磁力,f(x)表示在x的条件下能够放多少个球(单调递减),求最大值
代码:
class Solution {
public int maxDistance(int[] position, int m) {
Arrays.sort(position);
int len = position.length;
int left = 1;
int right = position[len-1];
while (left < right){
int mid = left + (right - left)/2;
int numBall = calcuBall(position, mid);
if (numBall < m){
right = mid;
}else if (numBall >= m){
left = mid + 1;
}
}
return left - 1;
}
public int calcuBall(int[] position, int maxPower){
int num = 1;
int start = 0;
for (int i = 1; i < position.length; i++){
if (position[i] - position[start] >= maxPower){
num++;
start = i;
}
}
return num;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了