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
Example 1:
Input:
[1,12,-5,-6,50,3]
3
Output:
15.667
Explanation:
(-6 + 50 + 3) / 3 = 15.667
Example 2:
Input:
[5]
1
Output:
5.000
Notice
It's guaranteed that the size of the array is greater or equal to k.
分析
这道题目是一个很好的题目,原来二分法不仅仅可以在对给定的数组进行二分,而且也可以对要求的结果进行二分查找。
我们立马能想到O(n^2)的解法,就是遍历所有可能的区间,然后记录最大的结果,但是这样会超时。
那么有没有复杂度更好的解法呢?
首先,如果在数组中求一个区间的平均值,那么这个平均值是否一定有一个范围?当然是肯定的,数组中任意区间的平均值一定是在数组的最大值maxNum和最小值minNum之间。好的,那么我们现在知道了最终要求的结果的一个范围,怎么一步一步找到这个结果呢?
这个时候可以使用二分查找的办法,首先在[minNum, maxNum]中搜索,令middle = (maxNum + minNum)/2,那么我们就判断是否存在连续长度大于等于k的区间使得该区间的平均值大于等于middle,如果有的话我们就继续搜索(middle, maxNum),没有的话就搜索(minNum, middle)。知道最后搜索的区间maxNum - minNum < eps,那么其实就是最终的结果。
好的,那到了这一步我们就需要解最终的一个问题,就是如何在数组中判断是否存在连续长度大于等于k的区间的平均值大于等于middle呢?
可以先把问题简化成,将数组中所有的值都减去middle,那么问题就转换成了数组中是否存在连续长度大于等于k的区间之和大于0。我们使用leftMinSum[i]代表区间[0, i]中区间[0, n](n <= i)最小的和,那么如果存在[m, i+k]区间m <= i +1使得区间的和大于等于0。那么一定有Sum[i+k] - leftMinSum[i] > 0。否则的话[0, i]中最小的区间和都不满足条件,那么肯定不存在m,使得[m , i +k]的区间之和大于0。所以从前之后遍历数组,计算是否存在这样的条件使得Sum[i+k] - leftMinSum[i] > 0 即可。
Code
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) {
// write your code here
int len = nums.size();
double maxNum = nums[0];
double minNum = nums[0];
for (int i = 1; i <= len; i ++)
{
maxNum = max(maxNum, nums[i]);
minNum = min(minNum, nums[i]);
}
while (maxNum - minNum > 1e-5)
{
double middle = (maxNum + minNum)/2;
if (canFind(nums, k, middle))
{
minNum = middle;
}
else
{
maxNum = middle;
}
}
return maxNum;
}
bool canFind(vector<int>& nums, int k, double average)
{
int len = nums.size();
vector<double> dec(len, 0);
for (int i = 0; i < len; i ++)
{
dec[i] = nums[i] - average;
}
double leftMinSum = 0;
double leftSum = 0;
double rightSum = 0;
for (int i = 0; i < k; i ++)
{
rightSum = rightSum + dec[i];
}
for (int i = k; i < len; i ++)
{
if (rightSum - leftMinSum >= 0)
{
return true;
}
rightSum += dec[i];
leftSum += dec[i-k];
leftMinSum = min(leftMinSum, leftSum);
}
if (rightSum - leftMinSum >= 0)
return true;
return false;
}
double min(double a, double b)
{
if (a - b <= 0)
return a;
return b;
}
double max(double a, double b)
{
if (a -b >= 0)
return a;
return b;
}
};
运行效率
Your submission beats 15.00% Submissions!