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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/roufoo/article/details/86989658
  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;
    }
};

猜你喜欢

转载自blog.csdn.net/roufoo/article/details/86989658