Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述
- 给你一个由
n个元素组成的整数数组nums和一个整数k。 - 请你找出平均数最大且 长度为
k的连续子数组,并输出该最大平均数。 - 任何误差小于
10-5的答案都将被视为正确答案。 - 示例 1:
- 输入: nums = [1,12,-5,-6,50,3], k = 4
- 输出: 12.75
- 解释: 最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
- 示例 2:
- 输入: nums = [5], k = 1
- 输出: 5.00000
- 提示:
n == nums.length1 <= k <= n <= 100000-10000 <= nums[i] <= 10000
二、思路分析:
- 常规思路
- 要求返回子序列中最大的平均值,遍历数组,找出数组中的所有子序列
- 计算子序列的平均值,保存到定义好的数组中
- 对数组按照降序排序
- 返回数组第一位值即平均值最大
- 思路没问题的,执行代码也没问题,操作都正确,但是,你以为这样就完了?
提交试试? - 对的,执行所有测试用例的时候
超时了,说明时间复杂度太高,那只能换一种方式了。。。 - 优化计算时间版本:
- 两个连续的子序列只有前后两项不同,中间的值都是一样的,所以中间的值不需要重复计算他们的和,这样会省去很多重复计算的时间,所以不回出现超时问题
- 第一个子序列和第二个子序列之间有相同部分,第二个子序列的和其实就是
第一个子序列的和减去当前子序列的第一项的前面一项,再加上第一个子序列最后一项的后面一项 - 所以我们可以先将第一个子序列的和求出来,然后后面的子序列和根据前面一个子序列的和加减得到
- 特殊情况:当
k=nums.length时,数组只有一个子序列,就是他本身,所以不需要求后面的和了,返回数组的平均值即可 - 后面有子序列的,利用
Math.max方法,比较返回一个最大的值,他的平均值肯定也是最大的 - 也可以定义一个数组变量,将所有的和保存起来,再返回数组最大值的平均值,只是这样时间和内存消耗会大一些
三、AC 代码:
- 第一种:求出每个子序列的平均值,返回最大的
function findMaxAverage(nums: number[], k: number): number {
let result = [];
for(let i = 0; i <= nums.length - k; i++){
let copy = JSON.parse(JSON.stringify(nums));
let group = copy.splice(i, k);
let count = 0;
for(let j = 0; j < group.length; j++){
count += group[j]
}
result.push((count / k).toFixed(5));
}
result.sort((a, b) => { return b - a })
return result[0]
};
- 第二种:通过每次减去前面的数和加上后面的数求和,这样的话每次只修改两个值就能计算下一个子序列的和了,当
k值很大时,不用将所有的值都重复的加起来了,极大的缩短了计算时间,所以当然就不回超时啦~
function findMaxAverage(nums: number[], k: number): number {
// let result = [];
// 第一个子序列的和
let sum = 0;
for(let i = 0; i < k; i++){
sum += nums[i];
}
// 只有一个子序列,即子序列就是本身
if(k === nums.length) return sum / k;
// let count = sum
// 最大和
let sumMax = sum;
for(let i = 1; i <= nums.length - k; i++){
sum = sum - nums[i - 1] + nums[i + k - 1];
// result.push(sum);
sumMax = Math.max(sumMax, sum)
}
// sumMax = Math.max(count, ...result)
return sumMax / k
};
四、总结:
- 题目在不考虑时间复杂度的情况下其实不难,但是想通过所有的测试用例,还是需要精简一下计算过程。去除掉无效的重复计算,才能缩短执行时间,才不会产生超时问题
- 希望以后做这种滑动窗口问题能先想到,避免重复计算
- 更多解题方式,移步题解区