子数组最大平均数 I

318 阅读1分钟

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.length
    • 1 <= k <= n <= 100000
    • -10000 <= nums[i] <= 10000

二、思路分析:

  • 常规思路
    • 要求返回子序列中最大的平均值,遍历数组,找出数组中的所有子序列
    • 计算子序列的平均值,保存到定义好的数组中
    • 对数组按照降序排序
    • 返回数组第一位值即平均值最大
  • 思路没问题的,执行代码也没问题,操作都正确,但是,你以为这样就完了?提交试试?
  • 对的,执行所有测试用例的时候超时了,说明时间复杂度太高,那只能换一种方式了。。。
  • 优化计算时间版本:
    • 两个连续的子序列只有前后两项不同,中间的值都是一样的,所以中间的值不需要重复计算他们的和,这样会省去很多重复计算的时间,所以不回出现超时问题
    • 第一个子序列和第二个子序列之间有相同部分,第二个子序列的和其实就是第一个子序列的和减去当前子序列的第一项的前面一项,再加上第一个子序列最后一项的后面一项
    • 所以我们可以先将第一个子序列的和求出来,然后后面的子序列和根据前面一个子序列的和加减得到
    • 特殊情况:当k=nums.length时,数组只有一个子序列,就是他本身,所以不需要求后面的和了,返回数组的平均值即可
    • 后面有子序列的,利用Math.max方法,比较返回一个最大的值,他的平均值肯定也是最大的
    • 也可以定义一个数组变量,将所有的和保存起来,再返回数组最大值的平均值,只是这样时间和内存消耗会大一些

image.png

三、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]
};

image.png

  • 第二种:通过每次减去前面的数和加上后面的数求和,这样的话每次只修改两个值就能计算下一个子序列的和了,当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
};

image.png

四、总结:

  • 题目在不考虑时间复杂度的情况下其实不难,但是想通过所有的测试用例,还是需要精简一下计算过程。去除掉无效的重复计算,才能缩短执行时间,才不会产生超时问题
  • 希望以后做这种滑动窗口问题能先想到,避免重复计算
  • 更多解题方式,移步题解区