算法:二分搜索

LeetCode题目

力扣 难度
1011. 在 D 天内送达包裹的能力 🟠
410. 分割数组的最大值 🔴
875. 爱吃香蕉的珂珂 🟠
剑指 Offer II 073. 狒狒吃香蕉 🟠

解题分析

这类题目可能的解空间在一个范围内,不停二分遍历可能解。找到一个可行解后还要继续找,直到“最小值”的可行解。类比到二分搜索算法中就是满足条件的最左值。

以”410. 分割数组的最大值“问题为例,可能解空间在“最大的单个元素”和“所有元素和”之间。然后二分查找可行解空间,一个可行解是分组数等于或者小于目标组数。“小于目标组数”成立的原因是可以继续拆分,拆分后子数组元素和一定满足条件,显然这不一定是最优解。找到可行解后如果继续优化条件不成立,那么上一次的可行解就是最优解。

从可行解中找最左值“二分搜索”中边界条件很容易出错。比如下面找最左值的方式:

        while (left < right) {
   
            int mid = left + (right - left) / 2;

            int splits = split(nums, mid);
            if (splits > m) {
   
                // 如果分割数太多,说明「子数组各自的和的最大值」太小,此时需要将「子数组各自的和的最大值」调大
                // 下一轮搜索的区间是 [mid + 1, right]
                left = mid + 1;
            } else {
   
                // 下一轮搜索的区间是上一轮的反面区间 [left, mid]
                right = mid;
            }
        }

个人更喜欢下面的方式,容易理解,不易出错,缺点是代码有点冗余。

while(left <= right) {
   
	int mid = left + (right - left) / 2;
	int groups = split(nums, mid);

	if(groups > k) {
    // 分组和太小,分组数大于预定值
		left = mid + 1;
	} else if(groups < k) {
    // 分组和太大,分组数小于预定值
		right = mid - 1;
	} else {
    // 可能分组数 < k 走不到这里,因为 split 是贪心算法。但实际上可以从 < k 拆分到 k 组
		if(mid == max || split(nums, mid -1) > k) {
   
			return mid;
		} else {
   
			right = mid - 1;
		}
	}
}
package org.stone.study.algo.ex202403;  
  
/**  
 * [410. 分割数组的最大值](https://leetcode.cn/problems/split-array-largest-sum/)
 * * 给定一个非负整数数组 nums 和一个整数 k ,你需要将这个数组分成 k 个非空的连续子数组。  
 * 设计一个算法使得这 k 个子数组各自和的最大值最小。  
 */  
public class SplitArrayWithMinSum {
     
  
    public static void main(String[] args) {
     
        int[] nums = new int[] {
   7,2,5,10,8};  
        int k = 2;  
        // ans: 18  
        System.out.println("ans:" + new SplitArrayWithMinSum().splitArray(nums, k));  
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值