LintCode 617: Maximum Average Subarray II (Binary Search 经典难题!!!)

  1. Maximum Average Subarray II

Given an array with positive and negative numbers, find the maximum average subarray which length should be greater or equal to given length k.

Example
Given nums = [1, 12, -5, -6, 50, 3], k = 3

Return 15.667 // (-6 + 50 + 3) / 3 = 15.667

Notice
It’s guaranteed that the size of the array is greater or equal to k.

思路1:
用prefix数组,直接枚举,但复杂度O(N^2)。过不了大数据。
注意不用i从0到len,j从i到n。实际上i从k-1到n, j从0到i-k+1就可以了。
代码如下:

class Solution {
public:
    /**
     * @param nums: an array with positive and negative numbers
     * @param k: an integer
     * @return: the maximum average
     */
    double maxAverage(vector<int> &nums, int k) {
        int len = nums.size();
        if (len == 0 || len < k) return 0;
       
        vector<double> prefixSum(len, 0);
        prefixSum[0] = nums[0];
        for (int i = 1; i < len; ++i) {
            prefixSum[i] = prefixSum[i - 1] + nums[i];
        }

        double maxAve = prefixSum[k - 1] / k;
        
        for (int i = k - 1; i < len; ++i) {
            for (int j = 0; j <= i - k + 1; ++j) {
               if (j == 0) {
                    int tempLen = i + 1;
                    if (maxAve * tempLen < prefixSum[i]) {
                        maxAve = prefixSum[i] / tempLen;
                    }
                }
                else {
                    double tempSum = prefixSum[i] - prefixSum[j - 1];
                    int tempLen = i - j + 1;
                    if (maxAve * tempLen < tempSum) {
                        maxAve = tempSum / tempLen;
                    }
                }

            }
        }
        return maxAve;
    }
};

思路2:
类似思路1,但不用数组。这个是参考的网上的答案。但也过不了大数据。

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        double preSum = accumulate(nums.begin(), nums.begin() + k, 0);
        double sum = preSum, res = preSum / k;
        for (int i = k; i < nums.size(); ++i) {
            preSum += nums[i];
            sum = preSum;
            if (sum > res * (i + 1)) res = sum / (i + 1);
            for (int j = 0; j <= i - k; ++j) {
                sum -= nums[j];
                if (sum > res * (i - j)) res = sum / (i - j);
            }
        }
        return res;
    }
};

思路3:
Binary Search试探着找到最大平均值T。即我们要找到最大的T = (A[left] + … + A[right]) / (right – left + 1),且right – left + 1>= k。
因为所求的T一定是位于数组最大值和最小值之间。
如果我们定义B[i]=A[i]-T,那么该问题就等价于求找到一个最大的B[left]+…+B[right]>=0,且right-left+1>=k。这个可以在O(N)时间内解决,代码见canFind()。
我们用Binary Search不断得到新的mid后,不断调用canFind()。这样总的复杂度是O(nlogm)。这里m是nums[]最大值和最小值的差。

注意:

  1. minLeftSum的值一定要初始化为0,而不是DOUBLE_MAX。这是因为如果k==len,那么canFind()里面第2个for()不会执行,如果初始化为DOUBLE_MAX的话,接下来if()判断中一定返回false。
    为什么初始化minLeftSum=0就可以呢?如果k==len,那在下面的if判断中就取决于rightSum的值,如果rightSum>=0,返回true,否则false。这是符合逻辑的。
class Solution {
public:
    /**
     * @param nums: an array with positive and negative numbers
     * @param k: an integer
     * @return: the maximum average
     */
    double maxAverage(vector<int> &nums, int k) {
        int len = nums.size();
        if (len == 0 || len < k) return 0;
       
        double maxAve = nums[0], minAve = nums[0], midAve = nums[0];
        
        for (int i = 1; i < len; ++i) {
            maxAve = max(maxAve, (double)nums[i]);
            minAve = min(minAve, (double)nums[i]);
        }

        while(maxAve - minAve > 1e-5) {
            midAve = minAve + (maxAve - minAve) / 2;
            if (canFind(nums, k, midAve)) {
                //the midAve is too small
                minAve = midAve;
            } else {
                //the midAve is too large
                maxAve = midAve;
            }
        }
        
        return minAve;
    }
    
private: 
    bool canFind(vector<int> &nums, int k, double mid) {
        double rightSum = 0, leftSum = 0;
        double minLeftSum = 0; //numeric_limits<double>::max();
        int len = nums.size();
        
        for (int i = 0; i < k; ++i) {
            rightSum += nums[i] - mid;
        }
        
        for (int i = k; i < len; ++i) {
            if (rightSum - minLeftSum >= 0) return true;

            rightSum += nums[i] - mid;
            leftSum += nums[i - k] - mid;
            minLeftSum = min(minLeftSum, leftSum);
        }
        
        //need to consider the case of i == len - 1
        if (rightSum - minLeftSum >= 0) return true;

        return false;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值