
给定整数数组和固定长度 k,找出长度为 k 的连续子数组中平均值最大的那个,并返回该平均值;关键在于避免双重循环的低效累加,改用 o(n) 滑动窗口技巧。
给定整数数组和固定长度 k,找出长度为 k 的连续子数组中平均值最大的那个,并返回该平均值;关键在于避免双重循环的低效累加,改用 o(n) 滑动窗口技巧。
在解决 LeetCode 643. Maximum Average Subarray I 时,核心目标是:**在所有长度为 k 的连续子数组中,找到元素和最大的那个,再除以 k 得到最大平均值**。看似简单,但初学者常因边界处理、变量重用或逻辑嵌套错误导致结果偏差(如测试用例 `[1,12,-5,-6,50,3], k=4` 返回 `12.0` 而非正确答案 `12.75`)。❌ 原代码的主要缺陷分析
你提供的实现存在三个关键问题:
-
内层循环范围错误:
j 实际只遍历了 <code>k−1个元素(漏掉第 k 个),应改为j 或更清晰的 <code>j ; -
累加变量未重置:
s在外层循环中未初始化为 0,导致每次迭代都延续上一次的和,造成严重累积误差; -
最大值更新时机错误:在内层循环中反复调用
Math.max(max, s),此时s还未完成当前子数组的完整求和,逻辑错位。
修正后的暴力解法(O(nk))如下:
public double findMaxAverage(int[] nums, int k) {
int n = nums.length;
double maxSum = Integer.MIN_VALUE;
for (int i = 0; i <= n - k; i++) {
double sum = 0;
for (int j = i; j < i + k; j++) { // ✅ 正确覆盖 k 个元素
sum += nums[j];
}
maxSum = Math.max(maxSum, sum);
}
return maxSum / k;
}✅ 推荐解法:滑动窗口(O(n) 时间复杂度)
暴力法在大规模输入下会超时(如 n=10⁵)。高效解法利用「滑动窗口」思想——先计算首个窗口 [0, k-1] 的和,之后每向右滑动一位,仅需 减去左端元素、加上右端新元素,避免重复计算。
优化实现(简洁清晰版):
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;
// 滑动窗口:i 为新窗口左端,i+k-1 为右端
for (int i = 1; i <= n - k; i++) {
sum = sum - nums[i - 1] + nums[i + k - 1]; // ✅ O(1) 更新
maxSum = Math.max(maxSum, sum);
}
return (double) maxSum / k;
}? 为什么用
long?
防止int溢出(如nums[i]较大且k较大时,子数组和可能超过Integer.MAX_VALUE)。强制转double再除法,确保浮点精度。
⚠️ 注意事项与最佳实践
-
边界处理:
n 时题目保证不会出现,但实际工程中建议添加 <code>if (n ; -
精度安全:始终先求最大整数和,最后统一转
double除法,避免中间步骤浮点误差或过早截断; -
可读性优先:相比双指针 while 循环变体(如原答案中较复杂的
l/r版本),上述for循环形式更直观、不易出错; -
测试验证:对
nums = [1,12,-5,-6,50,3], k = 4,窗口依次为:[1,12,-5,-6]→2,[-5,-6,50,3]→42,[12,-5,-6,50]→51,[-6,50,3,1]→48→ 最大和为51→51/4 = 12.75。
掌握滑动窗口不仅是本题的关键,更是解决「固定长度子数组/子串最值」类问题的通用范式。从暴力到优化,本质是将冗余计算转化为增量更新——这是算法进阶的重要思维跃迁。

















