题目描述

✅ 643. 子数组最大平均数 I

image-20260929102027667

题意分析

在数组中选择长度恰好为 k 的连续子数组,返回最大的平均值。元素可以为负数,不能用空子数组代替长度要求;题目保证 1 <= k <= nums.length。

解法:滑动窗口维护有效区间

核心思路

[!blue]
所有候选长度都相同,比较平均值就等价于比较窗口和。 分母 k 为固定正数,因此先求最大的窗口和 maxSum,最后除以 k 即可,不必为每个窗口重复计算平均值。

先求下标 [0, k - 1] 的和,作为 sum 和 maxSum 的初值。从 i = k 开始,每轮加入 nums[i]、移出 nums[i - k],更新后 sum 恰好表示窗口 [i - k + 1, i] 的和。相邻窗口中间的 k - 1 项相同,只调整两端即可完成移动。

每次移动完成后更新 maxSum,它始终是所有已检查完整窗口中的最大和。第一个窗口由初始化覆盖,其余窗口各对应一个新的右端点,因此没有遗漏。必须用真实首窗初始化最大值,全部元素为负时,0 可能根本不是任何合法窗口的和。

最后先转成浮点数,再除以 k,保留平均值的小数部分。k = 1 时相当于寻找最大元素,k = n 时只有初始窗口,滑动循环不执行也能直接返回结果。

解题步骤

  1. 计算前 k 项之和,并初始化最大和。
  2. 从下标 k 开始,加当前项、减 i-k 位置的项。
  3. 更新最大和。
  4. 转为浮点数后除以 k。

代码实现

class Solution {
    public double findMaxAverage(int[] nums, int k) {
        int n = nums.length;
        long sum = 0;

        for (int i = 0; i < k; i++) {
            sum += nums[i];
        }

        // 以真实首窗初始化最大值,全部为负时也能正确比较。
        long maxSum = sum;

        for (int i = k; i < n; i++) {
            // 加入当前项并移出旧左端,保持窗口长度固定。
            sum += nums[i] - nums[i - k];

            if (sum > maxSum) {
                maxSum = sum;
            }
        }

        return maxSum * 1.0 / k;
    }
}
func findMaxAverage(nums []int, k int) float64 {
    sum := 0
    for i := 0; i < k; i++ {
        sum += nums[i]
    }
    // 以真实首窗初始化最大值,全部为负时也能正确比较。
    maxSum := sum

    for i := k; i < len(nums); i++ {
        // 加入当前项并移出旧左端,保持窗口长度固定。
        sum += nums[i] - nums[i-k]
        if sum > maxSum {
            maxSum = sum
        }
    }

    return float64(maxSum) / float64(k)
}

复杂度分析

  • 时间复杂度:$O(n)$,初始化与滑动合计线性。
  • 空间复杂度:$O(1)$,只维护当前和与最大和。

关键点总结

[!green]

  • 移出的是 i-k,不是新窗口左端 i-k+1。
  • 候选在加减完成后比较。
  • 最终转为浮点除法,避免整数截断。

易错点总结

[!yellow]

  • 最大和初始化零:全负输入会返回不存在的候选。
  • 只加入不移出:统计变成增长前缀。
  • 移出位置偏一:不再代表真实定长窗口。
  • 先做整数除法再转换:小数部分已经丢失。

相似题目

题目 难度 关联与区别
644. 子数组最大平均数 II 困难 本题长度恰为k,滑动维护总和即可;原题长度至少k,不能用单个固定窗口解决。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2021/27673400
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!