LeetCode 643:子数组最大平均数 I
-
- [1. 题目介绍](#1. 题目介绍)
- [2. 解题思路](#2. 解题思路)
-
- [2.1 解法一:暴力枚举](#2.1 解法一:暴力枚举)
- [2.2 解法二:滑动窗口(推荐)](#2.2 解法二:滑动窗口(推荐))
- [3. 示例代码](#3. 示例代码)
-
- [3.1 解法一:暴力枚举](#3.1 解法一:暴力枚举)
- [3.2 解法二:滑动窗口](#3.2 解法二:滑动窗口)
- [4. 拓展:滑动窗口可视化页面](#4. 拓展:滑动窗口可视化页面)
- [5. 总结](#5. 总结)

🎬 博主名称: 超级苦力怕
🔥 个人专栏: 《LeetCode 题解》
🚀 每一次思考都是突破的前奏,每一次复盘都是精进的开始!
本篇文章给大家带来的是 LeetCode 第 643 题------子数组最大平均数 I 。这是一道 滑动窗口的经典入门题 ,虽然题目以子数组求平均数为背景,但核心思想「固定长度窗口的滑动」是所有滑动窗口问题的通用解法,掌握后可以轻松解决类似问题。
本文将采用 Java 语言进行讲解,从暴力解法出发,逐步优化到滑动窗口,帮助大家深入理解滑动窗口的核心原理。话不多说,让我们开始吧!
1. 题目介绍
643. 子数组最大平均数 I
直达链接:LeetCode 643
给你一个由 n 个元素组成的整数数组 nums 和一个整数 k。
请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

2. 解题思路
这道题要求在数组 nums 中找到长度为 k 的连续子数组,计算其平均数,并返回最大的平均数。关键在于理解题意:我们需要遍历所有长度为 k 的子数组,计算每个子数组的平均数,然后返回最大值。
2.1 解法一:暴力枚举
算法思想:
- 枚举所有长度为
k的子数组 - 对每个子数组求和并计算平均数
- 取所有子数组中平均数的最大值
复杂度分析:
- 时间复杂度:O(n * k),需要遍历每个子数组的每个元素
- 空间复杂度:O(1)
2.2 解法二:滑动窗口(推荐)
核心思想:维护一个长度为 k 的滑动窗口,通过不断向右移动窗口,利用上一次窗口的结果快速计算新窗口的和,避免重复计算。
实现思路:计算出第一个窗口的和,那么从窗口向右滑动一位时,我们只需要考虑新进入窗口的元素和离开窗口的元素,其他元素都在窗口内,因此不需要重新求和。
核心代码:文字描述(以示例1为例:nums = [1,12,-5,-6,50,3], k = 4)
以示例1为例:nums = [1,12,-5,-6,50,3], k = 4
1. 初始化窗口 [0,3] -> [1,12,-5,-6]
- 窗口和: 1+12+(-5)+(-6) = 2
- 平均数: 2/4 = 0.5
2. 窗口向右滑动到 [1,4] -> [12,-5,-6,50]
- 进入窗口: 50 -> sum = 52
- 离开窗口: 1 -> sum = 51
- 平均数: 51/4 = 12.75
- 更新答案: maxAvg = max(0.5, 12.75) = 12.75
3. 窗口向右滑动到 [2,5] -> [-5,-6,50,3]
- 进入窗口: 3 -> sum = 54
- 离开窗口: 12 -> sum = 42
- 平均数: 42/4 = 10.5
最终答案: maxAvg = 12.75
复杂度分析
- 时间复杂度:O(n),每个元素最多被访问两次
- 空间复杂度:O(1),只使用常数额外空间
- 相比暴力枚举(需要重复计算窗口内元素),滑动窗口利用了窗口移动的规律,避免了重复计算
3. 示例代码
3.1 解法一:暴力枚举
Java 实现:
java
class Solution {
public double findMaxAverage(int[] nums, int k) {
// 用于记录所有子数组中平均数的最大值
double maxAvg = Double.MIN_VALUE;
// 枚举所有长度为 k 的子数组的起始位置 i
for (int i = 0; i <= nums.length - k; i++) {
// 统计当前子数组的和
int sum = 0;
for (int j = i; j < i + k; j++) {
sum += nums[j];
}
// 计算当前子数组的平均数
double avg = (double) sum / k;
// 更新最大平均数
maxAvg = Math.max(maxAvg, avg);
}
// 返回最大平均数
return maxAvg;
}
}
3.2 解法二:滑动窗口
Java 实现:
java
class Solution {
public double findMaxAverage(int[] nums, int k) {
// ans:记录遍历过程中遇到的最大窗口和
int ans = Integer.MIN_VALUE;
// sum:记录当前窗口内的元素和
int sum = 0;
// 遍历数组,枚举每个可能成为窗口右端点的位置 i
for (int i = 0; i < nums.length; i++) {
// 将当前元素加入窗口
sum += nums[i];
// 计算当前窗口的左端点位置
int left = i - k + 1;
// 如果左端点位置小于 0,说明窗口大小还不足 k
// 尚未形成第一个完整的窗口,继续下一轮循环
if (left < 0) {
continue;
}
// ===== 步骤 1:更新答案 =====
// 当窗口大小达到 k 时,比较并更新最大窗口和
ans = Math.max(ans, sum);
// ===== 步骤 2:将当前左端点元素移出窗口 =====
// 为下一轮循环做准备,下一轮窗口会向右移动一位
// 将即将离开窗口的元素从 sum 中减去
sum -= nums[left];
}
// 返回最大平均数 = 最大窗口和 / k
return (double) ans / k;
}
}
4. 拓展:滑动窗口可视化页面
视频播放页面下载地址:github仓库,如果打不开请选择:gitee仓库,在后续会不断更新使用到的HTML页面。

5. 总结
| 解法 | 时间复杂度 | 空间复杂度 | 推荐指数 |
|---|---|---|---|
| 滑动窗口 | O(n) | O(1) | ⭐⭐⭐⭐⭐ |
| 暴力枚举 | O(n * k) | O(1) | ⭐⭐⭐ |
核心要点:
- 滑动窗口是本题的最优解法,充分利用了窗口移动的特性
- 滑动窗口的核心思想:维护一个固定长度的窗口,窗口滑动时只需要考虑新进入的元素和离开的元素
- 注意边界条件的处理(窗口大小不足 k 时的情况)
