LeetCode 2090 题解:半径为 k 的子数组平均值,定长滑动窗口经典题一文搞懂

LeetCode 2090:半径为 k 的子数组平均值

    • [1. 题目介绍](#1. 题目介绍)
    • [2. 解题思路](#2. 解题思路)
    • [3. 示例代码](#3. 示例代码)
      • [3.1 解法一:暴力枚举](#3.1 解法一:暴力枚举)
      • [3.2 解法二:滑动窗口](#3.2 解法二:滑动窗口)
    • [4. 总结](#4. 总结)

🎬 博主名称: 超级苦力怕

🔥 个人专栏: 《LeetCode 题解》

🚀 每一次思考都是突破的前奏,每一次复盘都是精进的开始!


本篇文章讲解的是 LeetCode 第 2090 题------半径为 k 的子数组平均值 。这是一道典型的 定长滑动窗口 题目,题目要求我们以每个位置为中心,计算长度固定为 2k+1 的区间平均值。

本文将使用 Java 进行讲解,从暴力枚举出发,逐步过渡到滑动窗口,帮助你掌握这类"固定长度区间求和再计算答案"的常见解法。

1. 题目介绍

2090. 半径为 k 的子数组平均值

直达链接:LeetCode 2090

给你一个下标从 0 开始的整数数组 nums,以及一个整数 k

对于每个下标 i,如果它左边和右边都至少还有 k 个元素,那么就可以得到一个以下标 i 为中心、长度为 2k+1 的子数组,其平均值就是对应位置的结果;否则该位置结果为 -1

你需要返回一个长度为 n 的数组 avgs,其中 avgs[i] 表示下标 i 的半径为 k 的子数组平均值。注意这里使用的是 截断式整数除法

2. 解题思路

题目要求我们对每个位置 i,计算一个固定长度为 2k+1 的子数组平均值。

也就是说,只有当区间 [i-k, i+k] 合法存在时,才能计算答案;否则该位置直接记为 -1

由于窗口长度固定为 2k+1,我们不需要每次都重新统计整段区间的元素和,而是可以维护一个 定长滑动窗口

设当前窗口元素和为 sum,那么窗口对应的中心位置就是当前右端点减去 k,即:

center = i - k

当窗口长度达到 2k+1 时:

  • 当前中心位置的答案可以直接由 sum / (2k+1) 得到
  • 然后把窗口最左侧元素移出,为下一个位置做准备

解法一:暴力枚举

算法思想:

  • 枚举每一个下标 i
  • 判断它左右两侧是否都至少还有 k 个元素
  • 如果可以形成长度为 2k+1 的区间,就遍历该区间求和
  • 最后计算平均值并写入答案数组,否则记为 -1

复杂度分析:

  • 时间复杂度:O(n * (2k+1)),最坏情况下每个位置都要重新遍历整个区间
  • 空间复杂度:O(1),不计答案数组的额外空间

解法二:滑动窗口(推荐)

通过维护一个长度为 2k+1 的滑动窗口,我们就能在窗口右移时高效更新区间和。

实现思路

  • 先让元素不断进入窗口
  • 当窗口大小不足 2k+1 时,先继续扩张窗口
  • 当窗口大小达到 2k+1 时,就可以计算当前中心位置的平均值
  • 然后移出窗口最左侧元素,让窗口继续向右滑动

这样每个元素最多只会进入窗口一次、移出窗口一次,因此整体时间复杂度可以优化到 O(n)。

示例推演 (以示例 1 为例:nums = [7,4,3,9,1,8,5,2,6]k = 3

复制代码
窗口长度 = 2k + 1 = 7

1. 先累加前 7 个元素,形成窗口 [0,6]
   - 窗口内容:[7,4,3,9,1,8,5]
   - 元素和 sum = 37
   - 中心位置 = 3
   - avgs[3] = 37 / 7 = 5

2. 窗口右移一位,变成 [1,7]
   - 移出 7,加入 2
   - 新窗口内容:[4,3,9,1,8,5,2]
   - 元素和 sum = 32
   - 中心位置 = 4
   - avgs[4] = 32 / 7 = 4

3. 窗口继续右移一位,变成 [2,8]
   - 移出 4,加入 6
   - 新窗口内容:[3,9,1,8,5,2,6]
   - 元素和 sum = 34
   - 中心位置 = 5
   - avgs[5] = 34 / 7 = 4

4. 其余位置由于左右元素不足 k 个
   - 结果保持为 -1

最终答案:[-1,-1,-1,5,4,4,-1,-1,-1]

复杂度分析

  • 时间复杂度:O(n),每个元素至多参与一次加入窗口和一次移出窗口
  • 空间复杂度:O(1),不计答案数组的额外空间
  • 相比暴力枚举,滑动窗口避免了对固定区间的重复求和

3. 示例代码

3.1 解法一:暴力枚举

Java 实现:

java 复制代码
class Solution {
    public int[] getAverages(int[] nums, int k) {
        int n = nums.length;
        int[] avgs = new int[n];

        for (int i = 0; i < n; i++) {
            if (i - k < 0 || i + k >= n) {
                avgs[i] = -1;
                continue;
            }

            long sum = 0;
            for (int j = i - k; j <= i + k; j++) {
                sum += nums[j];
            }

            avgs[i] = (int) (sum / (k * 2 + 1));
        }

        return avgs;
    }
}

3.2 解法二:滑动窗口

核心思想 :维护一个长度为 2k+1 的窗口,遍历数组时动态更新窗口元素和,并在窗口长度达到要求后记录中心位置的答案。

java 复制代码
import java.util.Arrays;

class Solution {
    public int[] getAverages(int[] nums, int k) {
        int n = nums.length;
        int[] avgs = new int[n];
        Arrays.fill(avgs, -1);

        // s:记录当前窗口内的元素和,注意这里必须使用 long
        long s = 0;

        // 遍历数组,枚举每个可能成为窗口右端点的位置 i
        for (int i = 0; i < n; i++) {
            // ===== 步骤 1:将当前右端点元素加入窗口 =====
            s += nums[i];

            // 如果窗口大小还不足 2k+1,则继续扩张窗口
            if (i < k * 2) {
                continue;
            }

            // ===== 步骤 2:记录答案 =====
            // 当前窗口对应的中心位置是 i-k
            avgs[i - k] = (int) (s / (k * 2 + 1));

            // ===== 步骤 3:将窗口最左侧元素移出 =====
            s -= nums[i - k * 2];
        }

        return avgs;
    }
}

更简洁的理解

  • 窗口右端点是 i
  • i >= 2k 时,说明长度为 2k+1 的窗口已经形成
  • 此时中心位置就是 i-k
  • 记录答案后,移除窗口左端元素即可
java 复制代码
import java.util.Arrays;

class Solution {
    public int[] getAverages(int[] nums, int k) {
        int n = nums.length;
        int[] avgs = new int[n];
        Arrays.fill(avgs, -1);

        long sum = 0;
        int len = k * 2 + 1;

        for (int i = 0; i < n; i++) {
            sum += nums[i];

            if (i < len - 1) {
                continue;
            }

            avgs[i - k] = (int) (sum / len);
            sum -= nums[i - len + 1];
        }

        return avgs;
    }
}

4. 总结

解法 时间复杂度 空间复杂度 推荐指数
滑动窗口 O(n) O(1) ⭐⭐⭐⭐⭐
暴力枚举 O(n * (2k+1)) O(1) ⭐⭐⭐

核心要点:

  1. 本题的关键是识别出"以某个位置为中心的区间",本质上对应一个固定长度为 2k+1 的窗口
  2. 当窗口长度固定时,优先考虑使用滑动窗口来维护区间和
  3. 由于区间元素和可能较大,代码中要使用 long 来避免溢出
相关推荐
阿Y加油吧1 小时前
堆 / 优先队列专题二刷笔记:前 K 个高频元素 & 数据流的中位数
java·笔记·算法
MicroTech20251 小时前
微算法科技(NASDAQ :MLGO)量子图像加权平均滤波:以量子优势重构图像处理效率与精度
科技·算法·重构
jieyucx1 小时前
Go 语言函数入门:定义、参数、返回值
c++·算法·golang·入门·函数
Brilliantwxx1 小时前
【C++】认识标准库STL(1)
开发语言·c++·笔记·程序人生·算法
啦啦啦_99991 小时前
2. 梯度下降算法分类 & 梯度下降与正规方程对比
人工智能·算法·分类
CHANG_THE_WORLD1 小时前
gc.py 功能介绍:PDF 对象流还原工具(用于 pdfium 测试)
算法·pdf
li星野1 小时前
链表通关八题:从反转链表到两数相加,手撕LeetCode高频题
数据结构·学习·leetcode·链表
沙白猿1 小时前
代码随想录 28(动态规划)
算法·动态规划
wuweijianlove1 小时前
算法复杂度与工程性能的双重度量体系技术7
算法