半径为k的子数组平均值
题目要求:

这题是经典的定长滑动窗口.
因为:
- 半径是K
- 以i为中新
- 子数组长度固定为:
2k+1
一、暴力解法
class Solution {
public int[] getAverages(int[] nums, int k){
int[] ans = new int[n]
for(int i = 0;i<n;i++){
//左边界
int left = i-k;
//右边界
int right = i+k;
//无法形成完整窗口
if(left<0||right>n-1){
ans[i] = -1;
continue;
}
//计算窗口总和
long sum = 0;
for(int j = left;j<=right;j++){
sum += nums[j];
}
//求窗口的平均值
ans[i] = (int)(sum/2*k+1);
}
return ans;
}
}
二、暴力解法的时间复杂度
- 外层循环
for (int i = 0; i < n; i++)
执行n次 - 内层循环
for (int j = left; j <= right; j++)
执行2k+1次
所以总时间复杂度
O(n×(2k+1))
通常写成:
O(nk)
三、滑动窗口思想(核心)
通过观察暴力算法我们发现窗口中的部分元素会被重复计算,
为了不重复计算整个窗口,
我们可以通过移动窗口进行优化:
删除左边元素,加入右边元素
总结为
入->更新->出

以下是灵茶山艾府给出的Java版题解(O(n)定长滑动窗口)
class Solution {
public int[] getAverages(int[] nums, int k) {
int n = nums.length;
int[] avgs = new int[n];
Arrays.fill(avgs, -1);
long s = 0; // 维护窗口元素和(注意类型!)
for (int i = 0; i < n; i++) {
// 1. 进入窗口
s += nums[i];
if (i < k * 2) { // 窗口大小不足 2k+1
continue;
}
// 2. 记录答案
avgs[i - k] = (int) (s / (k * 2 + 1));
// 3. 离开窗口
s -= nums[i - k * 2];
}
return avgs;
}
}
代码中调用了Arrays工具类的fill方法对数组进行初始化
Arrays.fill(avgs,-1);
假如不使用该工具类的方法,我们需要遍历整个数组来逐个赋值:
for(int i = 0;i<nums.length;i++){
avgs[i] = -1;
}
四、GPT算法流程
1. 初始化答案数组
默认全部:
-1
因为有些位置无法形成完整窗口
2. 特殊情况
如果2*k+1>n
说明连一个完整窗口都没有
直接全部返回-1。
3. 先计算第一个窗口和
4. 开始滑动窗口
每次:
- 加上右边
- 减去左边
并计算中心位置平均值
五、GPT版的Java代码实现
class Solution {
public int[] getAverages(int[] nums, int k) {
int n = nums.length;
// 结果数组
int[] ans = new int[n];
// 默认填充 -1
Arrays.fill(ans, -1);
// 窗口长度
int len = 2 * k + 1;
// 连一个完整窗口都没有
if (len > n) {
return ans;
}
// 用 long 防止溢出
long sum = 0;
// 1. 先计算第一个窗口和
for (int i = 0; i < len; i++) {
sum += nums[i];
}
// 第一个窗口中心
ans[k] = (int)(sum / len);
// 2. 滑动窗口
for (int right = len; right < n; right++) {
// 新加入元素
sum += nums[right];
// 移除左边元素
sum -= nums[right - len];
// 当前窗口中心
int center = right - k;
ans[center] = (int)(sum / len);
}
return ans;
}
}
一、第一处区别是GPT进行了特殊情况的处理
if(len>n){
return ans;
}
而灵茶山艾府并没有做单独处理
而是直接进入到窗口的计算
if (i < k * 2) { // 窗口大小不足 2k+1
continue;
}
如果没有形成窗口就直接跳过后面的代码提前进入下一个for循环。
for循环结束以后返回数组avgs
这样数组长度小于窗口长度,无法形成窗口的时候,滑动窗口求和的代码就不会被执行,
循环结束以后avgs并没有变动,返回的avgs每个值都是-1正好处理了这种特殊情况
二、第二处区别是GPT特别处理了第一个窗口
// 1. 先计算第一个窗口和
for (int i = 0; i < len; i++) {
sum += nums[i];
}
// 第一个窗口中心
ans[k] = (int)(sum / len);
然后再进入滑动窗口的逻辑
for(int right = len;right<n;right++){
//新加入元素
sum += nums[right];
//移除左边元素
sum -= nums[right-len];
//当前窗口中心
int center = right-k;
ans[center] = (int)(sum/len);
}
return res;
}
浙公网安备 33010602011771号