day124—二分查找—最小化数组中的最大值(LeetCode-2439)

题目描述

给你一个下标从 0 开始的数组 nums ,它含有 n 个非负整数。

每一步操作中,你需要:

  • 选择一个满足 1 <= i < n 的整数 i ,且 nums[i] > 0
  • nums[i] 减 1 。
  • nums[i - 1] 加 1 。

你可以对数组执行 任意 次上述操作,请你返回可以得到的 nums 数组中最大值 最小 为多少。

示例 1:

复制代码
输入:nums = [3,7,1,6]
输出:5
解释:
一串最优操作是:
1. 选择 i = 1 ,nums 变为 [4,6,1,6] 。
2. 选择 i = 3 ,nums 变为 [4,6,2,5] 。
3. 选择 i = 1 ,nums 变为 [5,5,2,5] 。
nums 中最大值为 5 。无法得到比 5 更小的最大值。
所以我们返回 5 。

示例 2:

复制代码
输入:nums = [10,1]
输出:10
解释:
最优解是不改动 nums ,10 是最大值,所以返回 10 。

提示:

  • n == nums.length
  • 2 <= n <= 105
  • 0 <= nums[i] <= 109

解决方案:

问题目标

给定一个数组,可以通过操作(将某个元素减1,同时将其左侧相邻元素加1)来调整数组,目标是最小化调整后数组的最大值

算法框架

1. 核心思想:二分查找答案

  • 我们不知道最小化的最大值是多少,但可以猜测一个值

  • 如果猜测值太小,无法通过操作使所有元素不超过它

  • 如果猜测值足够大,总能满足要求

  • 使用二分查找找到最小的满足条件的值

2. 二分查找部分

  • left 初始为 -1(确保答案比它大)

  • right 初始为数组最大值(这是肯定可行的上限)

  • 不断二分,检查中间值 mid 是否可行

  • 最终 right 就是最小可行值

3. 关键:check 函数(贪心验证)

检查给定限制 limit 是否可行:

  • 右向左遍历数组(因为只能向左传递数值)

  • 维护 dx 变量:表示需要向左传递的"超额"部分

  • 对于每个位置 i,计算当前值加上传递来的值

  • 如果超过 limit,超额部分继续向左传递

  • 最后检查第一个元素加上传递来的值是否不超过 limit

直观例子

假设 nums = [3, 7, 1, 6]limit = 5

  • 从右向左处理:

    • i=3: 6 ≤ 5? false,超额1传递给左边

    • i=2: 1 + 1 = 2 ≤ 5,无超额

    • i=1: 7 ≤ 5? false,超额2传递给左边

    • i=0: 3 + 2 = 5 ≤ 5,可行

算法特点

  • 时间复杂度:O(n log M),其中M是数组最大值

  • 空间复杂度:O(1)

  • 利用了单调性:如果limit可行,那么更大的limit也一定可行

  • 贪心策略最优:从右向左尽量传递超额值是最有效的

函数源码:

cpp 复制代码
class Solution {
public:
    bool check(vector<int>nums,int limit){
        long long dx=0;
        for(int i=nums.size()-1;i>0;i--){
            long long newNum=nums[i]+dx;
            dx=max(newNum-limit,0LL);
        }

        return nums[0]+dx<=limit;
    }

    int minimizeArrayValue(vector<int>& nums) {
        int max=*max_element(nums.begin(),nums.end());
        int left=-1;
        int right=max;

        while(left+1<right){
            int mid=(left+right)/2;
            if(check(nums,mid)){
                right=mid;
            }else{
                left=mid;
            }

        }
        return right;
    }
};
相关推荐
freexyn5 小时前
Matlab入门自学七十四:坐标系转换,直角坐标、极坐标和球坐标的转换
开发语言·算法·matlab
咱就是说不配啊5 小时前
3.20打卡day34
数据结构·c++·算法
小张会进步5 小时前
数组:二维数组
java·javascript·算法
佑白雪乐5 小时前
LCR 175. 计算二叉树的深度
算法·深度优先
阿Y加油吧5 小时前
力扣打卡day07——最大子数组和、合并区间
算法
想吃火锅10056 小时前
【leetcode】105. 从前序与中序遍历序列构造二叉树
算法·leetcode·职场和发展
圣保罗的大教堂6 小时前
leetcode 3567. 子矩阵的最小绝对差 中等
leetcode
2401_831824966 小时前
嵌入式C++驱动开发
开发语言·c++·算法
靠沿6 小时前
【优选算法】专题十八——BFS解决拓扑排序问题
算法·宽度优先
cui_ruicheng6 小时前
C++数据结构进阶:哈希表实现
数据结构·c++·算法·哈希算法·散列表