2090. 半径为 k 的子数组平均值
题意
对数组中的每个位置 i,取以它为中心、向左右各延伸 k 个元素的连续子数组,计算这 2k + 1 个数的整数平均值。如果某一侧没有足够的元素,该位置的答案就是 -1。
样例 1
解释:下标 0、1、2 的前面不足 3 个元素,下标 6、7、8 的后面不足 3 个元素,所以这些位置都是 -1。其余三个位置的平均值分别为:
avg[3] = (7+4+3+9+1+8+5) / 7 = 37 / 7 = 5;avg[4] = (4+3+9+1+8+5+2) / 7 = 4;avg[5] = (3+9+1+8+5+2+6) / 7 = 4。
样例 2
解释:下标 0 的半径为 0 的子数组只包含 100000,因此 avg[0] = 100000 / 1 = 100000。
样例 3
解释:下标 0 的前后都不足 100000 个元素,因此 avg[0] = -1。
解题思路(前缀和)
先计算前缀和,令 preSum[i] 表示从 nums[0] 到 nums[i] 的总和。以 i 为中心的窗口范围是 [i-k, i+k]:
- 当
i-k > 0,窗口和为preSum[i+k] - preSum[i-k-1]。 - 当
i-k == 0,窗口从数组开头算起,窗口和就是preSum[i+k]。
只有 k <= i < n-k 的位置能形成完整窗口。前缀和使用 long long,因为数组元素的总和可能超过 int 的范围。
我的代码
时间复杂度 O(n),空间复杂度 O(n),主要空间用在前缀和与返回数组上。
滑动窗口解法
窗口长度固定为 2k + 1,中心每次右移一格,窗口也只是右移一格——出窗口、进窗口的各只有一个元素,边走边维护窗口和即可,不需要前缀和数组:
- 先累加出第一个窗口
[0, 2k]的和,答案从中心i = k开始有效; - 每轮记录
ans[i] = sum / len后,窗口右移:加上右端新进来的nums[i+k+1],减去左端出去的nums[i-k]; - 两侧凑不满
2k + 1个元素的位置保持-1(ans初始化为-1); - 窗口和用
long long,防止元素总和超过int范围。
时间复杂度 O(n),空间复杂度 O(1)(不算返回数组)。相比前缀和解法省掉了 O(n) 的前缀和数组,这也是定长窗口最典型的"进一个、出一个"套路。