LeetCode - 644 子数组最大平均数 II
目录
题目来源
题目描述
示例
提示
题目解析
算法源码
题目来源
644. 子数组最大平均数 II - 力扣(LeetCode)
题目描述
给定一个包含 n 个整数的数组nums,找到最大平均值的连续子序列,且长度大于等于 k。并输出这个最大平均值。
示例
输入 | nums = [1,12,-5,-6,50,3], k = 4 |
输出 | 12.75 |
说明 |
当长度为 5 的时候,最大平均值是 10.8, 当长度为 6 的时候,最大平均值是 9.16667。 所以返回值是 12.75。 |
输入 | nums = [1,12,-5,-6,50,3,90,80,-100], k = 4 |
输出 | 55.75 |
说明 | 无 |
提示
- 1 <= k <= n <= 10,000
- 数组nums中的元素范围是 [-10,000, 10,000]
- 答案的计算误差小于 10^-5
题目解析
本题还是比较难,在leetcode中也是hard级别。
本题最简单的解题思路是,暴力枚举出nums的所有长度大于等于k的连续子序列,然后分别求解这些连续子序列的平均数,返回最大的。
这里,我们可以用双重for循环来枚举出所有长度大于等于k的连续子序列,外层是连续子序列的左侧起点,内层是连续子序列的右侧终点,但是这个时间复杂度是O(n^2),对于 1 ≤ n ≤ 10000 的数量级来说,是肯定会超时的。
因此上面思路不可取。
本题目前最优的解法如下:
第一步、通过二分法,找出所求子序列的可能的平均值。
这里,为什么要用二分法呢?
举个例子,有一个数组[1,2,3],请思考:
- 它的平均值会大于3吗?
- 它的平均值会小于1吗?
答案很显然,都不会。因为该数组的最大值为3,因此这个数组的平均值最大只可能为3,不可能超过3;同理这个数组的最小值为1,因此这个数组的平均值最小只能为1,不可能小于1。
因此,我们很容易得出一个结论,一个数组nums的平均值avg,必然满足:
min(nums) <= avg <= max(nums)
因此,我们可以用二分法,在min(nums)和max(nums)区间内找到一个符合要求平均值。
那么二分查找,何时结束呢?
更具题目备注:
答案的计算误差小于 10^-5
即,当二分法的查找区间,两端距离小于10^-5,就可以结束二分查找了。
嗯嗯,现在,我们已经通过二分查找,找到一个可能是子序列最大平均值的midAvg了,现在该如何验证这个midAvg是不是符合要求的呢?
符合啥要求呢?
即数组nums中所有的长度>=k的连续子序列的平均值都<= midVal。
此时midVal就是题解。
但是,上面验证方案,需要求解所有长度>=k的连续子序列,并求平均值。这不是我们所期望的。
我们可以反向思考一下,如果存在一个长度>=k的连续子序列,其平均值 > midVal,那么是不是说当前这个midVal就不是一个符合要求,并且,还有透露出一个重要信息,那就是这个midVal取小了!!
因此下次二分查找时,我们缩小查找范围,因该将此时的midVal作为新范围的左边界,这样再次二分时,取得的新midVal就会变大。
同理,如果所有的长度大于等于k的连续子序列的平均值都 < midVal,则说明该midVal也不是一个符合要求的,并且取大了,因此下次二分查找时,我们应该缩小右边界,让此时的midVal作为新的右边界,这样下次继续二分时,新midVal就会变小。
到此,关于midVal的取值逻辑已经阐述完毕,现在最大的问题是:
上面的逻辑,我们可能需要找出长度大于等于k的所有的连续子序列,然后求平均值,这个在暴力枚举时已经说过了,会超时。
因此,我们需要一个算法,可以高效的找出长度大于等于k的所有的连续子序列。
这里用到了动态规划的前缀和思想。请看下面例子:
有一个数组nums = [a1, a2, a3, a4, a5, ... , aN],现在要求从其中找到一个长度大于等于k的子数组。
首先,我们利用动态规划前缀和思想,求出以每一个数组元素为结尾的前缀和,公式如下
- dp[0] = nums[0]
- dp[i] = dp[i-1] + nums[i]
dp[i]表示0~i范围内所有元素的和。
那么nums中一个长度等于k的子数组是不是可以理解为:
从0~i区间内,挖去0~i-k区间,剩余的 i-k ~ i 不就是一个长度为k子数组吗
如果要我们求解长度等于k的子数组的和,那不就是:
dp[i] - dp[i-k]
但是现在,本题要求解的是:
长度 大于等于 k 的子数组,且该子数组的和要最大
首先,长度大于等于k的实现很简单,就是将
从0~i区间内,挖去0~i-x区间,其中x>=k,这样的话剩下的区间不就是一个大于等于k的子数组嘛
之后,是子数组的和最大,这个实现和被挖去的区间有关:
现在被挖去的区间可能为:
0~0
0~1
0~2
...
0~i-k
如果想让剩余区间的和最大,那么被挖去的区间的和要最小!!
即,在上面被挖去的区间内找到一个最小和的区间,而这个最小和的区间我们可以在求解前缀和的过程中记录下来,具体实现看下面代码。
现在,我们已经得到了一个 最大和 且 长度>=k的 子数组。
但是现在我们需要求解的是 最大平均值 且 长度 >= k 的子数组。
所以,现在的问题就是,把对最大平均值的求解,转化为对:最大和的求解。
这个很简单,只要把nums数组元素 都减去 midVal 即可。
原理是,本来我们是要,求解nums 某个区间内元素和的平均值,现在假设该区间的元素和平均值是avg,那么是不是等价于 验证: (a1 + a2 + a3 + ... + aN) / N == avg
再变化下,是不是等价于
a1 + a2 + a3 + ... + aN == N * avg
再变化下,是不是等价于
(a1 - avg) + (a2 - avg) + (a3 - avg) + ... + (aN - avg) == 0
如果avg取大了,则 (a1 - avg) + (a2 - avg) + (a3 - avg) + ... + (aN - avg) < 0
如果avg取小了,则 (a1 - avg) + (a2 - avg) + (a3 - avg) + ... + (aN - avg) > 0
JS算法源码
function getResult(nums, k) {let minAvg = Infinity;let maxAvg = -Infinity;for (let num of nums) {minAvg = Math.min(num, minAvg);maxAvg = Math.max(num, maxAvg);}const diff = 1e-5;while (maxAvg - minAvg >= diff) {let midAvg = (minAvg + maxAvg) / 2;if (check(nums, k, midAvg)) {minAvg = midAvg;} else {maxAvg = midAvg;}}return minAvg.toFixed(2);
}function check(nums, k, avg) {let sum = 0;for (let i = 0; i < k; i++) {sum += nums[i] - avg;}if (sum >= 0) {return true;}let pre_sum = 0;let min_pre_sum = 0;for (let i = k; i < nums.length; i++) {sum += nums[i] - avg;pre_sum += nums[i - k] - avg;min_pre_sum = Math.min(pre_sum, min_pre_sum);if (sum - min_pre_sum >= 0) {return true;}}return false;
}// 用例1
console.log(getResult([1, 12, -5, -6, 50, 3], 4));
// 用例2
console.log(getResult([1, 12, -5, -6, 50, 3, 90, 80, -100], 4));
Java算法源码
public class Main {public static void main(String[] args) {// int k = 4;// int[] nums = {1, 12, -5, -6, 50, 3};int k = 4;int[] nums = {1, 12, -5, -6, 50, 3, 90, 80, -100};System.out.println(getResult(nums, k));}public static String getResult(int[] nums, int k) {double minAvg = Integer.MAX_VALUE;double maxAvg = Integer.MIN_VALUE;for (int num : nums) {minAvg = Math.min(num, minAvg);maxAvg = Math.max(num, maxAvg);}double diff = 1e-5;while (maxAvg - minAvg >= diff) {double midAvg = (minAvg + maxAvg) / 2;if (check(nums, k, midAvg)) {minAvg = midAvg;} else {maxAvg = midAvg;}}return String.format("%.2f", minAvg).toString();}public static boolean check(int[] nums, int k, double avg) {double sum = 0;for (int i = 0; i < k; i++) {sum += nums[i] - avg;}if (sum >= 0) return true;double preSum = 0;double minPreSum = 0;for (int i = k; i < nums.length; i++) {sum += nums[i] - avg;preSum += nums[i - k] - avg;minPreSum = Math.min(preSum, minPreSum);if (sum - minPreSum >= 0) {return true;}}return false;}
}
LeetCode - 644 子数组最大平均数 II相关推荐
- LeetCode 643. 子数组最大平均数 I
1. 题目 给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示例 1: 输入: [1,12,-5,-6,50,3], k = 4 输出: 12.75 解释: 最大平均 ...
- leetcode 643. 子数组最大平均数 I(滑动窗口)
给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示例: 输入:[1,12,-5,-6,50,3], k = 4 输出:12.75 解释:最大平均数 (12-5-6+5 ...
- [Leetcode] 643. 子数组最大平均数 I java
给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示例 1: 输入: [1,12,-5,-6,50,3], k = 4 输出: 12.75 解释: 最大平均数 (12- ...
- 【LeetCode】第643题——子数组最大平均数I(难度:简单)
[LeetCode]第643题--子数组最大平均数I(难度:简单) 题目描述 解题思路 代码详解 注意点 题目描述 给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示 ...
- Leetcode滑窗系列(java):643. 子数组最大平均数 I
Leetcode滑窗系列(java):643. 子数组最大平均数 I(新手小白仅供参考) 题目来源 leetcode 题目描述 个人思路 创建一个滑窗,将其值的和作为作为判断基准 然后滑窗的左右边界各 ...
- 【LeetCode】643. 子数组最大平均数 I
class Solution1 {/*643. 子数组最大平均数 I给你一个由 n 个元素组成的整数数组 nums 和一个整数 k .请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数 ...
- 【每日一题】 643. 子数组最大平均数 I
[每日一题] 643. 子数组最大平均数 I 避免每日太过咸鱼,一天搞定一道LeetCode算法题 一.题目描述 难度: 简单 给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大 ...
- 643. 子数组最大平均数 I
链接:643. 子数组最大平均数 I 题解:https://leetcode-cn.com/problems/maximum-average-subarray-i/solution/jing-dian ...
- 643、子数组最大平均数 I
643.子数组最大平均数 I 题目: 给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示例: 输入:[1,12,-5,-6,50,3], k = 4 输出:12.75 ...
最新文章
- 目标形体形状轮廓重建:ICCV2019论文解析
- [Beta]第二次 Scrum Meeting
- 李宏毅机器学习课程4~~~分类:概率生成模型
- java 代码同步_Java同步代码块 转
- 命令界面:使用Java中的动态API处理Redis
- B 站崩了,受害程序员聊聊
- 从卓越工程的角度看微软中国开发团队的成长 (一)
- 位运算 —— 一个数二进制形式尾端为 0 的个数
- JavaScript - 正则表达之二
- 关于"舆情监测"关键词在百度搜索中的相关数分析
- 用户场景法,设计测试用例
- 机载激光雷达原理与应用科普(七)
- 数据预测模型_如何根据已有数据得出预测模型?线性回归公式来帮你!
- [经验] 系统封装常见问题大总结(非官方)
- unity 裙子摆动_【Unity Shader】摇摆的小草——顶点动画
- C语言编程 | 转义字符
- Python读写Excel文件-1
- 【转载】R语言dplyr包学习笔记(吐血整理宇宙无敌详细版)
- 1577 例题3 数字转换(LOJ10155) 约数计算 树上最长链(两次找最大深度)
- Verilog设计(二):分频电路设计