题目描述
一个数组长度为n,找到这个数组长度至少为k的子串的最大平均值。
样例
input [1,12,-5,-6,50,3], k = 4
Output: 12.75
Explanation:
当子串长度为4时,最大的子串和为12.75
当子串长度为5时,最大的子串和为10.8
当子串长度为6时,最大的子串和为9.16667
因此返回12.75
算法1
二分法 $O(nlogM)$ M为二分的范围
对长度至少为k的最大子串的平均值进行二分,二分的范围为start=Integer.MIN_VALUE end=Integer.MAX_VALUE,x=(start+end)/2,如果有
(nums[i]+nums[i+1]+...+nums[j])/(j-i+1)>x
=>nums[i]+nums[i+1]+...+nums[j]>x*(j-i+1)
=>(nums[i]-x)+(nums[i+1]-x)+...+(nums[j]-x)>0
则结果的范围在[x, end],否则结果的范围为[start, x]
判断是否存在(nums[i]-x)+(nums[i+1]-x)+...+(nums[j]-x)>0
可以在O(n)的时间内完成。对数组进行一次遍历,维护两个量:当前子段和cur,和当前子段前i-k个数的和pre。如果pre<0,则更新cur=cur-pre, pre=0. 每一步判断是否有cur>0,若有则返回true。
时间复杂度分析:二分的时间复杂度为O(logM),判断是否存在某个平均值的子段的复杂度为log(n),总时间复杂度为O(nlogM)
Java 代码
class Solution {
public double findMaxAverage(int[] nums, int k) {
double s=Integer.MIN_VALUE, e=Integer.MAX_VALUE;
while(e-s>1e-6) {
double mid = (e+s)/2;
if(greater(nums, k, mid)) s=mid;
else e=mid;
}
return s;
}
public boolean greater(int[] nums, int k, double x) {
int n = nums.length;
double[] a = new double[n];
for(int i=0; i<n; i++) a[i]=nums[i]-x;
double cur=0, pre=0;
for(int i=0; i<k; i++) cur+=a[i];
if(cur>0) return true;
for(int i=k; i<n; i++) {
cur+=a[i];
pre+=a[i-k];
if(pre<0) {
cur-=pre;
pre=0;
}
if(cur>0) return true;
}
return false;
}
}
alternative implementation