LeetCode-Go 题解:643. Maximum Average Subarray I(固定长度滑动窗口求最大平均值)
【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go
导读
本文讲解 LeetCode 第 643 题「Maximum Average Subarray I」在 LeetCode-Go 仓库中的 Go 实现。该题是**固定长度滑动窗口(Fixed-Size Sliding Window)**的入门级经典题目:在给定数组中寻找长度为 k 的连续子数组,使其平均值最大。读完本文你将掌握固定窗口的「先初始化、后滑动」双循环套路,理解为何用整数累加替代浮点累加,并能直接运行仓库内已配套的测试用例验证结果。
关联文档:leetcode/0643.Maximum-Average-Subarray-I/README.md
题目描述
Given an array consisting ofnintegers, find the contiguous subarray of given lengthkthat has the maximum average value. And you need to output the maximum average value.
示例 1:
Input: [1,12,-5,-6,50,3], k = 4 Output: 12.75 Explanation: Maximum average is (12-5-6+50)/4 = 51/4 = 12.75注意事项:
1 <= k <= n <= 30,000;- 数组中每个元素的取值范围为
[-10,000, 10,000]。
题目大意
给定n个整数,找出平均数最大且长度为k的连续子数组,并输出该最大平均数。
数据范围与数值溢出分析
在动手编码前,先分析一下数据边界,这直接决定了实现细节:
- 数组长度最大为
n = 30,000; - 每个元素绝对值最大为
10,000; - 因此窗口长度为
k时的最大窗口和绝对值不超过30,000 × 10,000 = 3 × 10⁸。
该数量级完全可以安全存入 Go 的int类型(64 位平台为 int64,即便 32 位平台的 int32 上限约 2.1 × 10⁹ 也足以容纳),这就是实现中选择用int累加窗口和的数值依据。若换成浮点数float64反复累加再求均值,反而会在连续运算中引入舍入误差,且效率更低。
解题思路:固定长度滑动窗口
原文档给出的思路非常精炼:「简单题。循环一次,扫描数组过程中累加窗口大小为 k 的元素值。不断更新这个最大值。循环结束求出平均值即可。」
拆解开来,这是固定长度滑动窗口的标准三步法:
- 初始化窗口:先累加数组前
k个元素,得到第一个长度为k的窗口和,记为当前最大和; - 滑动窗口:从下标
i = k开始遍历到数组末尾,每向右移动一格,窗口移除最左边的元素nums[i-k]、加入新元素nums[i],得到下一个窗口和:sum = sum - nums[i-k] + nums[i]这一步的时间复杂度为 O(1),相比每次重新计算
k个元素之和(O(k))大大节省了开销; - 更新最大值:每次滑动后,用
maxSum = max(maxSum, sum)维护历史最大窗口和。
遍历结束后,maxSum是最大窗口和,由于所有窗口长度都是k,平均值最大等价于窗口和最大,因此最终结果直接除以k即可,无需在滑动过程中频繁做除法。
整个过程只扫描数组一次,时间复杂度 O(n),空间复杂度 O(1)。
代码实现(含注释)
仓库中 643. Maximum Average Subarray I.go 的完整实现如下:
package leetcode func findMaxAverage(nums []int, k int) float64 { sum := 0 // 1. 初始化窗口:累加前 k 个元素 for _, v := range nums[:k] { sum += v } maxSum := sum // 2. 滑动窗口:每步 O(1) 完成窗口和的更新 for i := k; i < len(nums); i++ { sum = sum - nums[i-k] + nums[i] maxSum = max(maxSum, sum) } // 3. 最大平均值 = 最大窗口和 / k return float64(maxSum) / float64(k) } func max(a, b int) int { if a > b { return a } return b }实现要点解读:
- 用
nums[:k]切片完成窗口初始化,代码简洁且不产生数据拷贝(Go 切片为视图); - 滑动时
nums[i-k]是被移出窗口的左端元素,nums[i]是新进入窗口的右端元素,两者一减一加即可得到新窗口和; - 最终使用
float64(maxSum) / float64(k)将整数和转换为浮点平均值。由于最大窗口和不超过 3 × 10⁸(见上文分析),转换过程精度无损; - 仓库没有依赖内置的
max(Go 1.21 之前标准库无此函数),而是自带了一个局部max辅助函数,保证代码在较低 Go 版本下也可直接编译运行。
测试用例与验证
仓库为本题配套了测试文件 643. Maximum Average Subarray I_test.go,采用「参数-答案」结构组织用例:
type para643 struct { nums []int k int } type ans643 struct { one float64 }核心测试用例与题目示例一一对应:
qs := []question643{ { para643{[]int{1, 12, -5, -6, 50, 3}, 4}, ans643{12.75}, }, }即输入[1,12,-5,-6,50,3]、k = 4,期望输出12.75,对应窗口(12-5-6+50)/4 = 51/4 = 12.75。该用例同时覆盖了「窗口内包含负数」的场景,验证了算法在混合正负数数组下的正确性。
在仓库根目录执行以下命令即可运行测试:
go test -v -run Test_Problem643 ./leetcode/0643.Maximum-Average-Subarray-I/为什么归类于滑动窗口与数组专题
在 LeetCode-Go 仓库的专题索引中,本题同时出现在两处:
- 滑动窗口专题:位于滑动窗口经典题列表内;
- 数组专题:作为数组类基础题收录。
这反映了题目的双重属性:它是最朴素的固定窗口滑动模型(左右边界同步移动、窗口长度恒定),是理解后续复杂滑动窗口问题(如 0239. Sliding Window Maximum 的单调队列、0480. Sliding Window Median 的堆结构)的起点;同时它也是数组连续子区间求值的基础训练。仓库中同类「固定窗口长度求极值」的姊妹题还包括 1984. Minimum Difference Between Highest and Lowest of K Scores、1052. Grumpy Bookstore Owner 等,均可对比学习。
复杂度与进阶思考
| 指标 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 数组仅被完整扫描一次,每步窗口更新为 O(1) |
| 空间复杂度 | O(1) | 仅使用sum、maxSum两个常数级变量,无额外数据结构 |
两种可选思路的对比:
- 暴力法:枚举每个长度为
k的子数组并重新求和,时间复杂度 O(n·k),在最坏情况(n = 30,000,k ≈ 15,000)下将产生约 4.5 亿次加法运算,明显劣于滑动窗口; - 前缀和(Prefix Sum):先构造前缀和数组,再用
prefix[i+k] - prefix[i]求窗口和,时间复杂度同为 O(n),但需要 O(n) 的额外空间。本题对空间有更优要求,故滑动窗口方案更佳。
延伸思考:若要求「长度至少为 k」的最大平均子数组,问题升级为 LeetCode 644 题(Maximum Average Subarray II),需要引入二分答案 + 前缀和技巧,这正是从本题出发可以继续深挖的方向。
小结
- 核心结论:平均值最大等价于窗口和最大,因为所有候选子数组长度固定为
k; - 核心套路:先累加前
k个元素初始化窗口,再通过sum = sum - nums[i-k] + nums[i]以 O(1) 代价滑动窗口并维护最大值; - 核心实践:用整数累加避免浮点误差,仅在最后一步除以
k转换为浮点结果。
通过阅读 题目题解文档、实现源码 与 测试用例,你可以完整复现并验证这一经典固定滑动窗口解法。
【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考