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 a binary tree:
1
/
-5 11
/ /
1 2 4 -2
return the node 11
.
二分法做,一开始你就假设一个average在最大值和最小值的中间,然后看这个average合不合格,小了去右半找,大了去左半找。
合不合格的函数里,先存一个sum数组,保存前i个num - average的和,这样sum[n]-sum[m]就代表(n~m这几个连续数字各自与ave的差值)的和。如过出现了sum[n]-sum[m] > 0的情况,就说明你的average其实可以取得更大来获得一个更好的最大值。
这个函数里还可以进一步优化从n方判断函数变为n判断函数。针对某个具体的sum[i],你可以跟的被减数一定要跟i隔了k个以上,也就是被减数index在i-k或之前。那么你只要试着找到sum[0]~sum[i-k]之间的最小数来作为减数就好了,因为如果这个最小数都不行,其他数就更不可能做到让sum[i]-减数>0了!所以一直用一个min_pre的变量来保存sum[0]~sum[i-k]之间的最小值,每次随着i变大不断更新该数字。
部分解释可见http://blog.csdn.net/qq_34153219/article/details/56298842
九章实现:
public class Solution { /** * @param nums an array with positive and negative numbers * @param k an integer * @return the maximum average */ 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; } }
我的实现(思路模仿了的):
public class Solution { /* * @param nums: an array with positive and negative numbers * @param k: an integer * @return: the maximum average */ public double maxAverage(int[] nums, int k) { // write your code here if (nums == null) { return Double.NEGATIVE_INFINITY; } double left = Double.POSITIVE_INFINITY; double right = Double.NEGATIVE_INFINITY; for (int i = 0; i < nums.length; ++i) { if (nums[i] < left) { left = (double)nums[i]; } if (nums[i] > right) { right = (double)nums[i]; } } double eps = 1e-6; while (left + eps < right) { double mid = left + (right - left) / 2; if (canBigger(mid, nums, k)) { left = mid; } else { right = mid; } } return left; } private boolean canBigger(double avg, int[] nums, int k) { double[] sums = new double[nums.length]; sums[0] = nums[0] - avg; for (int i = 1; i < sums.length; i++) { sums[i] = sums[i - 1] + nums[i] - avg; } double min_prev = 0; for (int i = k - 1; i < sums.length; i++) { if (sums[i] - min_prev >= 0) { return true; } min_prev = Math.min(min_prev, sums[i - k + 1]); } return false; } }