LeetCode 643:子数组最大平均数 I

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) ⭐⭐⭐

核心要点:

  1. 滑动窗口是本题的最优解法,充分利用了窗口移动的特性
  2. 滑动窗口的核心思想:维护一个固定长度的窗口,窗口滑动时只需要考虑新进入的元素和离开的元素
  3. 注意边界条件的处理(窗口大小不足 k 时的情况)
相关推荐
笨笨饿2 小时前
#65_反激电源
stm32·单片机·嵌入式硬件·算法·硬件工程·个人开发
wengqidaifeng2 小时前
数据结构:排序(下)---进阶排序算法详解
数据结构·算法·排序算法
MicroTech20252 小时前
突破单机量子计算限制:MLGO微算法科技的新型分布式量子算法模拟平台实现高效验证
科技·算法·量子计算
没有天赋那就反复2 小时前
C++里面引用参数和实参的区别
开发语言·c++·算法
wengqidaifeng2 小时前
数据结构:排序(上)---基础排序算法详解
数据结构·算法·排序算法
Zlssszls2 小时前
机器人马拉松的第二年,比的是其背后的隐形赛场:具身训练工具链
算法·机器人
shylyly_2 小时前
sizeof 和 strlen的理解与区分
c语言·算法·strlen·sizeof
m0_743106462 小时前
【浙大&南洋理工最新综述】Feed-Forward 3D Scene Modeling(五)
人工智能·算法·计算机视觉·3d·几何学
kobesdu11 小时前
人形机器人SLAM:技术挑战、算法综述与开源方案
算法·机器人·人形机器人