LeetCode 1343 题解:定长滑动窗口经典入门题,从暴力枚举到高效优化一文搞懂

LeetCode 1343:大小为 K 且平均值大于等于阈值的子数组数目

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

🎬 博主名称: 超级苦力怕

🔥 个人专栏: 《LeetCode 题解》

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


本篇文章讲解的是 LeetCode 第 1343 题------大小为 K 且平均值大于等于阈值的子数组数目 。这是一道典型的 定长滑动窗口 入门题,题目表面上在考察平均值,实质上考察的是如何高效维护长度固定的区间和。

本文将使用 Java 进行讲解,从暴力枚举出发,逐步过渡到滑动窗口,帮助你理解这类题目的常见处理方式。

1. 题目介绍

1343. 大小为 K 且平均值大于等于阈值的子数组数目

直达链接:LeetCode 1343

给你一个整数数组 arr 和两个整数 k 以及 threshold

请你返回 arr 中长度为 k 且平均值大于等于 threshold 的子数组的数目。

2. 解题思路

题目要求统计所有长度为 k 的子数组中,平均值大于等于 threshold 的个数。

由于子数组长度固定为 k,判断平均值是否达标,其实可以转化为判断元素和是否满足:

sum >= k * threshold

这样就不需要真的去做除法,直接比较整数即可。

解法一:暴力枚举

算法思想:

  • 枚举所有长度为 k 的子数组
  • 对每个子数组遍历计算元素和
  • 判断元素和是否大于等于 k * threshold
  • 统计满足条件的子数组个数

复杂度分析:

  • 时间复杂度:O(n * k),需要遍历每个子数组的每个元素
  • 空间复杂度:O(1)

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

通过维护一个长度为 k 的滑动窗口,每次移动时,只需要更新进入窗口和离开窗口的元素。

实现思路:窗口每向右移动一位,窗口内大部分元素都不变,因此只需:

  • 减去离开窗口的元素
  • 加上进入窗口的元素

这样就能在 O(1) 的时间完成窗口和的更新,避免重复计算。

示例推演 (以示例 1 为例:arr = [2,2,2,2,5,5,5,8]k = 3threshold = 4

复制代码
以示例1为例:arr = [2,2,2,2,5,5,5,8], k = 3, threshold = 4  
目标:元素和 >= k * threshold = 3 * 4 = 12

1. 初始化窗口 [0,2] -> [2,2,2]
   - 元素和 s = 6,不满足条件

2. 窗口向右滑动到 [1,3] -> [2,2,2]
   - 进入窗口: 2,s = 8
   - 离开窗口: 2,s = 6
   - 不满足条件

3. 窗口向右滑动到 [2,4] -> [2,2,5]
   - 进入窗口: 5,s = 11
   - 离开窗口: 2,s = 9
   - 不满足条件

4. 窗口向右滑动到 [3,5] -> [2,5,5]
   - 进入窗口: 5,s = 14
   - 离开窗口: 2,s = 12
   - 满足条件!ans = 1

5. 窗口向右滑动到 [4,6] -> [5,5,5]
   - 进入窗口: 5,s = 17
   - 离开窗口: 2,s = 15
   - 满足条件!ans = 2

6. 窗口向右滑动到 [5,7] -> [5,5,8]
   - 进入窗口: 8,s = 23
   - 离开窗口: 5,s = 18
   - 满足条件!ans = 3

最终答案: ans = 3

复杂度分析

  • 时间复杂度:O(n),每个元素至多参与一次加入窗口和一次移出窗口
  • 空间复杂度:O(1),只使用常数额外空间
  • 相比暴力枚举,滑动窗口避免了对窗口内元素的重复求和

3. 示例代码

3.1 解法一:暴力枚举

Java 实现:

java 复制代码
class Solution {
    public int numOfSubarrays(int[] arr, int k, int threshold) {
        // 用于记录满足条件的子数组个数
        int ans = 0;
        
        // 目标元素和 = k * threshold
        int target = k * threshold;
        
        // 枚举所有长度为 k 的子数组的起始位置 i
        // 起始位置范围:0 到 arr.length - k
        for (int i = 0; i <= arr.length - k; i++) {
            // 统计当前子数组 arr[i, i+k) 的元素和
            int sum = 0;
            
            // 遍历当前窗口内的所有元素
            for (int j = i; j < i + k; j++) {
                sum += arr[j];
            }
            
            // 如果元素和 >= 目标值,则计数加一
            if (sum >= target) {
                ans++;
            }
        }
        
        // 返回满足条件的子数组个数
        return ans;
    }
}

3.2 解法二:滑动窗口

核心思想 :维护一个长度为 k 的窗口,遍历数组时动态更新窗口元素和,并在窗口长度达到 k 后判断当前区间是否满足条件。

java 复制代码
class Solution {
    public int numOfSubarrays(int[] arr, int k, int threshold) {
        // ans:记录满足条件的子数组个数
        int ans = 0;
        // s:记录当前窗口内的元素和
        int s = 0;
        
        // 遍历数组,枚举每个可能成为窗口右端点的位置 i
        // i 的取值范围:0 到 arr.length - 1
        for (int i = 0; i < arr.length; i++) {
            
            // ===== 步骤 1:将当前右端点元素加入窗口 =====
            s += arr[i];
            
            // 计算当前窗口的左端点位置
            // 当 i = k-1 时,left = 0,此时窗口大小正好为 k
            int left = i - k + 1;
            
            // 如果左端点位置小于 0,说明窗口大小还不足 k
            // 尚未形成第一个完整的窗口,继续下一轮循环
            if (left < 0) {
                continue;
            }

            // ===== 步骤 2:更新答案 =====
            // 当窗口大小达到 k 时,判断元素和是否满足条件
            // 元素和 >= k * threshold 等价于 平均值 >= threshold
            if (s >= k * threshold) {
                ans++;
            }
            
            // ===== 步骤 3:将当前左端点元素移出窗口 =====
            // 为下一轮循环做准备,下一轮窗口会向右移动一位
            // 记录即将离开窗口的元素
            s -= arr[left];
        }
        
        // 返回满足条件的子数组个数
        return ans;
    }
}

更简洁的写法:本质上和上面的实现完全一致,只是把窗口是否形成的判断写得更紧凑一些。

java 复制代码
class Solution {
    public int numOfSubarrays(int[] arr, int k, int threshold) {
        int ans = 0;
        int s = 0;
        
        for (int i = 0; i < arr.length; i++) {
            s += arr[i];
            
            if (i < k - 1) {
                continue;
            }
            
            if (s >= k * threshold) {
                ans++;
            }
            
            s -= arr[i - k + 1];
        }
        
        return ans;
    }
}

4. 总结

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

核心要点:

  1. 本题的关键是把"平均值判断"转化为"区间和判断"
  2. 由于窗口长度固定为 k,适合使用滑动窗口优化
  3. 当窗口长度达到 k 后,再开始统计答案
相关推荐
样例过了就是过了1 小时前
LeetCode热题100 单词拆分
c++·算法·leetcode·动态规划·哈希算法
王老师青少年编程1 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【跳跃与过河问题】:跳跳!
c++·算法·贪心·csp·信奥赛·跳跃与过河问题·跳跳
MediaTea1 小时前
ML:决策树的基本原理与实现
人工智能·算法·决策树·机器学习·数据挖掘
王老师青少年编程1 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【跳跃与过河问题】:独木桥
c++·算法·贪心·csp·信奥赛·跳跃与过河问题·独木桥
忡黑梨1 小时前
eNSP_DHCP配置
c语言·网络·c++·python·算法·网络安全·智能路由器
空中海1 小时前
Nacos3: 面试题库
java·面试·职场和发展
陈壮实的搬砖日记2 小时前
白话生成式推荐二:MiniOneRec之RQ-VAE
算法
陈壮实的搬砖日记2 小时前
白话生成式推荐二:MiniOneRec之SFT
算法
她说彩礼65万2 小时前
C语言 动态内存管理
c语言·开发语言·算法