这道题目呢,和之前的643类型差不多,但是更难。
首先这道题我看了Solution的解法,反正不是n2的复杂度,是 n*log(max-min) 的样子吧,一开始怎么做都不对,超时。。。直到直接跑了solution的代码(java),发现原因不是代码写错了,而是python特别慢!!!! 所以不是大神就别用python了,所以我大概还是以Java为主吧。
Given an array consisting of n integers, find the contiguous subarray whose length is greater than or equal to k that has the maximum average value. And you need to output the maximum average value.
Example 1:
Input: [1,12,-5,-6,50,3], k = 4
Output: 12.75
Explanation:
when length is 5, maximum average value is 10.8,
when length is 6, maximum average value is 9.16667.
Thus return 12.75.
Note:
1 <= k <= n <= 10,000.
Elements of the given array will be in range [-10,000, 10,000].
The answer with the calculation error less than 10-5 will be accepted.
解法上最简单的是暴力,但是很明显不通过,不可可能的,就不说了
这道题的思想就是二分猜测,首先需要预测的这个平均值肯定是介于最大值和最小值之间的,所以就以二分法的思想,不停的猜测就可以了,这个就是核心思想,进行二分法的需要操作log(max-min)次。
我们不停的猜测那个mid值(max和min的平均值)在给定的序列中能否满足,也就是说是否存在一个大于等于k的区间的平均值大于等于mid值,如果是,我们就可以把下限值min变成mid,反之改变max,就这样做,直到猜测范围小于0.00001就可以直接给出答案了(题目说了有误差的容许范围,小提示吧)。
另外一个难点在于如何在线性的时间内,判别nums当中是否存在这样的一段,使得平均值大于等于mid。做法也很简单:
顺序遍历数组nums(这里需要统一减去mid),统计两个值这里第一个值是sums,从位置0到当前位置j的和。另外一个是从0到某个位置i的和的最小值min_sum(其中i小于j,且i和j的长度大于等于k)。也就是等价于找一段和大于0的子区间,注意不是找最大,所以不用太严格。。
public class Solution {
public double findMaxAverage(int[] nums, int k) {
double max = Double.MIN_VALUE;
double min = Double.MAX_VALUE;
// 寻找最值
for (int n: nums) {
max = Math.max(max, n);
min = Math.min(min, n);
}
double last_mid = max, error = Integer.MAX_VALUE;
while (max-min > 0.00001) {
double mid = (max + min) / 2.0;
if (check(nums, mid, k))
min = mid;
else
max = mid;
error = Math.abs(last_mid - mid);
last_mid = mid;
}
return min;
}
// 判断这个区间里面,是否有一段大于等于K的长度的最长序列,满足要求,就是最大的累计和,减去最小的累积和
public boolean check(int[] nums, double mid, int k) {
double sum = 0, prev = 0, min_sum = 0;
for (int i = 0; i < k; i++)
sum += nums[i] - mid;
if (sum >= 0)
return true;
for (int i = k; i < nums.length; i++) {
sum += nums[i] - mid;
prev += nums[i - k] - mid;
min_sum = Math.min(prev, min_sum);
if (sum >= min_sum)
return true;
}
return false;
}
}