LintCode 最大平均值子数组

给出一个整数数组,有正有负。找到这样一个子数组,他的长度大于等于 k,且平均值最大。

注意事项

保证数组的大小 >= k

样例
给出 nums = [1, 12, -5, -6, 50, 3], k = 3

返回 15.667 // (-6 + 50 + 3) / 3 = 15.667

参考了别人的想法,利用二分法,依然不是对数组中元素的二分,而是对一个区间的二分,逐渐找到结果。题目所求平均值一定是在 数组中最小值min和最大值max之间的一个数,所以对区间[min,max]二分查找。
如何判断是在mid左侧继续查找还是右侧继续查找,我们需要判断对于当前的假设结果mid(即假设的平均值),在数组中是否存在一个长度大于等于k的子数组,其平均值比mid大。当然不能枚举,我们用一个辅助数组d,把原数组中每个数与mid的差的前缀和存储起来(利用前缀和的问题还有这个最接近零的子数组和),如果数组d中,存在d[i]-d[j]>=0,而且i-j>=k ,那么说明原数组nums[j] 到nums[i] 这个子数组的平均值大于等于mid,接下来就要在[mid,max]中继续找,否则在[min,mid]中继续找。在判断d[i]-d[j]>=0时,我们用一个最小是preMin来代替d[j],preMin指的是数组d中,在d[i]之前,并且满足i-j>=k的最小值。(因为我们要判断是否存在一个误差大于0,所以我们只关心任意两个误差前缀和相减之后的最大值,所以那个被减数越小越好)。
代码如下:

ublic class Solution {
    /*
     * @param nums: an array with positive and negative numbers
     * @param k: an integer
     * @return: the maximum average
     */
    public double maxAverage(int[] nums, int k) {
        // write your code here
        double max=nums[0],min=nums[0];
        for(int x: nums){
            if(x>max){
                max=x;
            }
            if(x<min){
                min=x;
            }
        }
        double avg=0.0;
        while(max-min>1e-6){
            boolean flag=false;
            avg=min+(max-min)/2.0;
            double[] d =new double[nums.length+1];
            d[0]=0.0;//数组d中d[0]存0,d[1]存nums中第1个数与avg的差...
            double preMin=d[0];
            for(int i=1;i<=nums.length;i++){
                d[i]=d[i-1]+nums[i-1]-avg;
                if(i>=k && d[i]-preMin>=0){
                    flag=true;
                    break;
                }
                if(i>=k){
                    preMin=Math.min(preMin,d[i-k+1]);
                }
            }
            if(flag){
                min=avg;
            }
            else{
                max=avg;
            }
        }
        return min;
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值