Related: LeetCode 643/644 - Maximum Average Subarray I/II
https://www.lintcode.com/en/problem/maximum-average-subarray/
http://www.jiuzhang.com/solutions/maximum-average-subarray/
http://blog.csdn.net/qq_34153219/article/details/56298842
一开始用了最简单的遍历循环法,时间复杂度为O(N^2),然后被提醒超时,上网查了之后做了一个小改动,用一个sum数组存储num[0]到num[i]的总和,这样算num[i]到num[j] 只要算sum[j]-sum[i]就好了。
https://www.lintcode.com/en/problem/maximum-average-subarray/
Given an array with positive and negative numbers, find the
maximum average subarray
which length should be greater or equal to given length k
.Notice
It's guaranteed that the size of the array is greater or equal to k.
Example
Given nums =
[1, 12, -5, -6, 50, 3]
, k = 3
Return
15.667
// (-6 + 50 + 3) / 3 = 15.667http://www.jiuzhang.com/solutions/maximum-average-subarray/
public double maxAverage(int[] nums, int k) { // Write your code here double l = Integer.MAX_VALUE, r = Integer.MIN_VALUE; for (int i = 0; i < nums.length; ++i) { if (nums[i] < l) l = nums[i]; if (nums[i] > r) r = nums[i]; } while (r - l >= 1e-6) { double mid = (l + r) / 2.0; if (check_valid(nums, mid, k)) { l = mid; } else { r = mid; } } return l; } private boolean check_valid(int nums[], double mid, int k) { int n = nums.length; double min_pre = 0; double[] sum = new double[n + 1]; sum[0] = 0; for (int i = 1; i <= n; ++i) { sum[i] = sum[i - 1] + nums[i - 1] - mid; if (i >= k && sum[i] - min_pre >= 0) { return true; } if (i >= k) min_pre = Math.min(min_pre, sum[i - k + 1]); } return false; }http://blog.csdn.net/gqk289/article/details/68952421
求值问题很多可以用二分,初始beg & end是数组的最小和最大值。
二分另一点在于valid函数。本题中为给定一个mid,判断是否存在长度大于等于k且平均值大于等于mid的子数组。sum[i]保存nums[i] - mid的和。如果sum[i] - min_pre >= 0的话说明当前子数组的平均值大于等于mid
http://blog.csdn.net/qq_34153219/article/details/56298842
一开始用了最简单的遍历循环法,时间复杂度为O(N^2),然后被提醒超时,上网查了之后做了一个小改动,用一个sum数组存储num[0]到num[i]的总和,这样算num[i]到num[j] 只要算sum[j]-sum[i]就好了。
- double maxAverage(vector<int>& nums, int k){
- double result;
- int i,j,num;
- int vectorLength=nums.size();
- double tmp[vectorLength];
- tmp[0]=0;
- for(i=1;i<vectorLength+1;i++){
- tmp[i]=tmp[i-1]+nums[i-1];
- }
- result=tmp[vectorLength]/vectorLength;
- for(j=vectorLength;j>=k;j--){
- for(i=0;i<vectorLength-k+1;i++){
- if(j-i>=k&&(tmp[j]-tmp[i])/(j-i)>result)
- result=(tmp[j]-tmp[i])/(j-i);
- else if(j-i<k)
- break;
- }
- }
- return result;
- }
个人理解:
1、一个数组的子数组的最大平均数一定在数组的最大值和最小值之间,所以二分法的第一步限定average位于[min,max]之中。
2、接下去要做的就是不断的缩小范围,直至max-min足够小(如1e-6),那我们就得到了想要的结果。
缩小范围的思想如下:
每一轮设置mid=(min+max)/2,然后将原数组中的每一个数减去这个mid,如果能找到(经过提醒,改正为:大于等于)k个相邻数的总和大于0的情况,那么说明最终结果一定比这个mid要更大,限定下一轮寻找范围在[mid,max]之中。反之在限定在[min,mid]之中。
那么在实际算法中我们需要解决的关键一步就是,如何判断“有(大于等于)k个相邻数的总和大于0的情况存在”。
首先,我们可以用sum数组来存储减掉mid值的原数组的各总和(sum[i]存储num[0]-mid到num[i-1]-mid的总和),当sum[i]存储的总和个数超过k时(即i>k),也就是说我们保证了这个子数组的长度达到k后,可以砍掉之前一些拖后腿的数。这些拖后腿的数在上述链接的代码中是用min_pre来实现的。当之前拖后腿的数值小于min_pre时,更新min_pre=sum[i - k + 1]。sum[i]存储的是num[0]~num[i-1]减去mid的总和,而min_pre存储的是num[0]~num[k]减掉mid的总和,这样sum[i]-min_pre得到的是sum[k+1]~sum[i-1],它所记录的总和个数也就是到num[i]为止能够找到的最大平均数 子数组的长度。