
给定整数数组和固定长度 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 <h3>✅ 推荐解法:滑动窗口(O(n) 时间复杂度)</h3><p>暴力法在大规模输入下会超时(如 <code>n=10⁵</code>)。高效解法利用「滑动窗口」思想——先计算首个窗口 <code>[0, k-1]</code> 的和,之后每向右滑动一位,仅需 <strong>减去左端元素、加上右端新元素</strong>,避免重复计算。</p><p>优化实现(简洁清晰版):</p><pre class="brush:php;toolbar:false;">public double findMaxAverage(int[] nums, int k) {
int n = nums.length;
// 计算第一个窗口和
long sum = 0;
for (int i = 0; i <blockquote><p>? <strong>为什么用 <code>long</code>?</strong><br>
防止 <code>int</code> 溢出(如 <code>nums[i]</code> 较大且 <code>k</code> 较大时,子数组和可能超过 <code>Integer.MAX_VALUE</code>)。强制转 <code>double</code> 再除法,确保浮点精度。</p></blockquote><h3>⚠️ 注意事项与最佳实践</h3>
-
边界处理:
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。
掌握滑动窗口不仅是本题的关键,更是解决「固定长度子数组/子串最值」类问题的通用范式。从暴力到优化,本质是将冗余计算转化为增量更新——这是算法进阶的重要思维跃迁。










