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. 1 <= k <= n <= 10,000.
  2. Elements of the given array will be in range [-10,000, 10,000].
  3. The answer with the calculation error less than 10-5 will be accepted.

这道题是之前那道 Maximum Average Subarray I 的拓展,那道题说是要找长度为k的子数组的最大平均值,而这道题要找长度大于等于k的子数组的最大平均值。加了个大于k的条件,情况就复杂很多了,之前只要遍历所有长度为k的子数组就行了,现在还要包括所有长度大于k的子数组。我们首先来看 brute force 的方法,就是遍历所有的长度大于等于k的子数组,并计算平均值并更新结 果res。那么先建立累加和数组 sums,结果 res 初始化为前k个数字的平均值,然后让i从 k+1 个数字开始遍历,此时的 sums[i] 就是前 k+1 个数组组成的子数组之和,我们用其平均数来更新结果 res,然后从开头开始去掉数字,直到子数组剩余k个数字为止,再用其平均值来更新解结果 res,通过这种方法,我们就遍历了所有长度大于等于k的子数组。这里需要注意的一点是,更新结果 res 的步骤不能写成 res = min(res, t / (i + 1)) 这种形式,会 TLE,必须要在if中判断 t > res * (i + 1) 才能 accept,写成 t / (i + 1) > res 也不行,必须要用乘法,这也说明了计算机不喜欢算除法吧,参见代码如下:

解法一:

class Solution {
public:
double findMaxAverage(vector<int>& nums, int k) {
int n = nums.size();
vector<int> sums = nums;
for (int i = ; i < n; ++i) {
sums[i] = sums[i - ] + nums[i];
}
double res = (double)sums[k - ] / k;
for (int i = k; i < n; ++i) {
double t = sums[i];
if (t > res * (i + )) res = t / (i + );
for (int j = i - k; j >= ; --j) {
t = sums[i] - sums[j];
if (t > res * (i - j)) res = t / (i - j);
}
}
return res;
}
};

我们再来看一种 O(n2) 时间复杂度的方法,这里对上面的解法进行了空间上的优化,并没有长度为n数组,而是使用了 preSum 和 sum 两个变量来代替,preSum 初始化为前k个数字之和,sum 初始化为 preSum,结果 res 初始化为前k个数字的平均值,然后从第 k+1 个数字开始遍历,首先 preSum 加上这个数字,sum 更新为 preSum,然后用当前 k+1 个数字的平均值来更新结果 res。和上面的方法一样,我们还是要从开头开始去掉数字,直到子数组剩余k个数字为止,然后用其平均值来更新解结果 res,那么每次就用 sum 减去 nums[j],就可以不断的缩小子数组的长度了,用当前平均值更新结果 res,注意还是要用乘法来判断大小,参见代码如下:

解法二:

class Solution {
public:
double findMaxAverage(vector<int>& nums, int k) {
double preSum = accumulate(nums.begin(), nums.begin() + k, );
double sum = preSum, res = preSum / k;
for (int i = k; i < nums.size(); ++i) {
preSum += nums[i];
sum = preSum;
if (sum > res * (i + )) res = sum / (i + );
for (int j = ; j <= i - k; ++j) {
sum -= nums[j];
if (sum > res * (i - j)) res = sum / (i - j);
}
}
return res;
}
};

下面来看一种优化时间复杂度到 O(nlg(max - min)) 的解法,其中 max 和 min 分别是数组中的最大值和最小值,是利用了二分搜索法,博主之前写了一篇 LeetCode Binary Search Summary 二分搜索法小结 的博客,这里的二分法应该是小结的第四类,也是最难的那一类,因为判断折半的方向是一个子函数,这里我们没有用子函数,而是写到了一起,可以抽出来成为一个子函数,这一类的特点就是不再是简单的大小比较,而是需要一些复杂的操作来确定折半方向。这里主要借鉴了蔡文森特大神的帖子,所求的最大平均值一定是介于原数组的最大值和最小值之间,所以我们的目标是用二分法来快速的在这个范围内找到要求的最大平均值,初始化 left 为原数组的最小值,right 为原数组的最大值,然后 mid 就是 left 和 right 的中间值,难点就在于如何得到 mid 和要求的最大平均值之间的大小关系,从而判断折半方向。我们想,如果已经算出来了这个最大平均值 maxAvg,那么对于任意一个长度大于等于k的数组,如果让每个数字都减去 maxAvg,那么得到的累加差值一定是小于等于0的,这个不难理解,比如下面这个数组:

[1, 2, 3, 4]   k = 2

我们一眼就可以看出来最大平均值 maxAvg = 3.5,所以任何一个长度大于等于2的子数组每个数字都减去 maxAvg 的差值累加起来都小于等于0,只有产生这个最大平均值的子数组 [3, 4],算出来才正好等于0,其他都是小于0的。那么可以根据这个特点来确定折半方向,我们通过 left 和 right 值算出来的 mid,可以看作是 maxAvg 的一个 candidate,所以就让数组中的每一个数字都减去 mid,然后算差值的累加和,一旦发现累加和大于0了,那么说明 mid 比 maxAvg 小,这样就可以判断方向了。

我们建立一个累加和数组 sums,然后求出原数组中最小值赋给 left,最大值赋给 right,题目中说了误差是 1e-5,所以循环条件就是 right 比 left 大 1e-5,然后算出来 mid,定义一个 minSum 初始化为0,布尔型变量 check,初始化为 false。然后开始遍历数组,先更新累加和数组 sums,注意这个累加和数组不是原始数字的累加,而是它们和 mid 相减的差值累加。我们的目标是找长度大于等于k的子数组的平均值大于 mid,由于每个数组都减去了 mid,那么就转换为找长度大于等于k的子数组的差累积值大于0。建立差值累加数组的意义就在于通过 sums[i] - sums[j] 来快速算出j和i位置中间数字之和,那么只要j和i中间正好差k个数字即可,然后 minSum 就是用来保存j位置之前的子数组差累积的最小值,所以当 i >= k 时,我们用 sums[i - k] 来更新 minSum,这里的 i - k 就是j的位置,然后判断如果 sums[i] - minSum > 0了,说明找到了一段长度大于等k的子数组平均值大于 mid 了,就可以更新 left 为 mid 了,我们标记 check 为 true,并退出循环。在 for 循环外面,当 check 为 true 的时候,left 更新为 mid,否则 right 更新为 mid,参见代码如下:

解法三:

class Solution {
public:
double findMaxAverage(vector<int>& nums, int k) {
int n = nums.size();
vector<double> sums(n + , );
double left = *min_element(nums.begin(), nums.end());
double right = *max_element(nums.begin(), nums.end());
while (right - left > 1e-) {
double minSum = , mid = left + (right - left) / ;
bool check = false;
for (int i = ; i <= n; ++i) {
sums[i] = sums[i - ] + nums[i - ] - mid;
if (i >= k) {
minSum = min(minSum, sums[i - k]);
}
if (i >= k && sums[i] > minSum) {check = true; break;}
}
if (check) left = mid;
else right = mid;
}
return left;
}
};

下面这种解法对上面的方法优化了空间复杂度 ,使用 preSum 和 sum 来代替数组,思路和上面完全一样,可以参加上面的讲解,注意这里我们的第二个if中是判断 i >= k - 1,而上面的方法是判断 i >= k,这是因为上面的 sums 数组初始化了 n + 1 个元素,注意坐标的转换,而第一个 if 中 i >= k 不变是因为j和i之间就差了k个,所以不需要考虑坐标的转换,参见代码如下:

解法四:

class Solution {
public:
double findMaxAverage(vector<int>& nums, int k) {
double left = *min_element(nums.begin(), nums.end());
double right = *max_element(nums.begin(), nums.end());
while (right - left > 1e-) {
double minSum = , sum = , preSum = , mid = left + (right - left) / ;
bool check = false;
for (int i = ; i < nums.size(); ++i) {
sum += nums[i] - mid;
if (i >= k) {
preSum += nums[i - k] - mid;
minSum = min(minSum, preSum);
}
if (i >= k - && sum > minSum) {check = true; break;}
}
if (check) left = mid;
else right = mid;
}
return left;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/644

类似题目:

Maximum Average Subarray I

参考资料:

https://leetcode.com/problems/maximum-average-subarray-ii/

https://leetcode.com/problems/maximum-average-subarray-ii/discuss/105498/c-binary-search-130ms

https://leetcode.com/problems/maximum-average-subarray-ii/discuss/105495/10-line-c-ac-barely-solution-on2

https://leetcode.com/problems/maximum-average-subarray-ii/discuss/105480/Java-solution-O(nlogM)-Binary-search-the-answer

https://leetcode.com/problems/maximum-average-subarray-ii/discuss/105484/C%2B%2B-solution-simple-improvement-to-brute-force-O(nk)

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Maximum Average Subarray II 子数组的最大平均值之二的更多相关文章

  1. [LeetCode] 644. Maximum Average Subarray II 子数组的最大平均值之二

    Given an array consisting of n integers, find the contiguous subarray whose length is greater than o ...

  2. [LeetCode] Maximum Average Subarray I 子数组的最大平均值

    Given an array consisting of n integers, find the contiguous subarray of given length k that has the ...

  3. leetcode 643. Maximum Average Subarray I 子数组最大平均数 I

    一.题目大意 https://leetcode.cn/problems/maximum-average-subarray-i/ 给你一个由 n 个元素组成的整数数组 nums 和一个整数 k . 请你 ...

  4. Leetcode643.Maximum Average Subarray I子数组的最大平均数1

    给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示例 1: 输入: [1,12,-5,-6,50,3], k = 4 输出: 12.75 解释: 最大平均数 (12- ...

  5. leetcode644. Maximum Average Subarray II

    leetcode644. Maximum Average Subarray II 题意: 给定由n个整数组成的数组,找到长度大于或等于k的连续子阵列,其具有最大平均值.您需要输出最大平均值. 思路: ...

  6. 643. Maximum Average Subarray I 最大子数组的平均值

    [抄题]: Given an array consisting of n integers, find the contiguous subarray of given length k that h ...

  7. Maximum Average Subarray II LT644

    Given an array consisting of n integers, find the contiguous subarray whose length is greater than o ...

  8. Maximum Average Subarray II

    Description Given an array with positive and negative numbers, find the maximum average subarray whi ...

  9. LC 644. Maximum Average Subarray II 【lock,hard】

    Given an array consisting of n integers, find the contiguous subarray whose length is greater than o ...

随机推荐

  1. 如何测试一个WEB的输入框?

    WEB输入框是B/S架构系统中页面使用非常频繁的控件,比如我们登录一个网站,输入 用户名和密码的控件都是输入框,比如使用百度搜索,在输入搜索内容的控件也是输入框,比如网购一个物品,我们需要输入购买的数 ...

  2. javaAPI中的常用 类 以及接口

    java.lang包中的常用类以及接口 类 1. Integer :Integer 类在对象中包装了一个基本类型 int 的值.Integer 类型的对象包含一个 int 类型的字段. 2. Math ...

  3. Linux下ping,telnet,ssh命令的比较

    ping工作在OSI模型的第三层,网络层. 主要用于测试到达目的主机的网络是否连接,不能检测某个端口是否开放. ping使用ICMP协议,不使用某个特定端口. 也可以 ping 域名 ,这样可以直接看 ...

  4. Java基础学习笔记二十三 Java核心语法之反射

    类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任 ...

  5. 福州大学W班-alpha冲刺评分

    作业链接 https://edu.cnblogs.com/campus/fzu/FZUSoftwareEngineering1715W/homework/1159 作业要求 1.前期准备 阅读学习&l ...

  6. beta冲刺总结

    前言: 经过一周的努力,对alpha版本进行不断的更新,得到了beta版本. 主要成员介绍: 成员 性格 优点 缺点 主要工作 黄紫仪 努力奋斗型 努力奋斗,爱学习 爱吐槽 功能点修改和部分界面修改, ...

  7. Beta阶段敏捷冲刺报告-DAY5

    Beta阶段敏捷冲刺报告-DAY5 Scrum Meeting 敏捷开发日期 2017.11.6 会议时间 12:00 会议地点 软工所 参会人员 全体成员 会议内容 乱序问题的解决,异常输入提示 讨 ...

  8. sqlserver之排序规则和ETL不支持sqlserverdatetime2的问题

    sqlserver的排序规则大概分为Windows 排序规则和 SQL Server 排序规则.数据在安装的时候,默认不设置会默认为SQL_Latin1_General_CP1_CI_AI.数据库在创 ...

  9. ArcGIS地图打印那些事

    记录了通过ArcGIS的PringTask进行地图打印,以及借助html2canvas实现屏幕截图的方法.两个方法各有适用的情景.过程中涉及的相关资源和问题给出链接,自行查看. 1.需求背景 地图打印 ...

  10. django报错invalid literal for int() with base 10: ''

    这种错误是因为模型类中某个字段的条件约束为int类型,但是给了一个字符串类型,所以报错,找到那个模型类的字段,并对应修改就好了.