题目链接
题目描述
给你一个整数数组 arr
和两个整数 k
和 threshold
,请你返回长度为 k
且平均值大于等于 threshold
的子数组数目。
解法分析:滑动窗口法
核心思路
该解法采用滑动窗口技术,通过维护一个长度为 k
的固定窗口,遍历数组时动态计算窗口内元素的和,并判断其平均值是否满足条件。具体步骤如下:
- 用滑动窗口遍历数组,窗口长度固定为
k
- 计算每个窗口的和,通过与
threshold * k
比较避免浮点数运算 - 统计满足条件的窗口数量
代码实现
class Solution:
def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int:
l = ans = s = 0 # 左指针、结果计数、窗口和
for r in range(len(arr)):
s += arr[r] # 累加当前元素到窗口和
# 窗口长度不足k时跳过判断
if r + 1 < k:
continue
# 判断窗口和是否≥threshold*k(等价于平均值≥threshold)
if s >= threshold * k:
ans += 1
# 滑动窗口:减去左边界元素,左指针右移
s -= arr[l]
l += 1
return ans
代码解析
-
初始化变量:
l
:滑动窗口左指针,初始为0ans
:满足条件的子数组数量,初始为0s
:当前窗口内元素的和,初始为0
-
遍历数组:
- 右指针
r
从0开始遍历每个元素 s += arr[r]
:将当前元素累加到窗口和中
- 右指针
-
窗口判断逻辑:
if r + 1 < k
:当窗口长度不足k时(如前k-1个元素),跳过判断if s >= threshold * k
:窗口长度达到k时,通过和与threshold*k
比较判断平均值是否达标
-
窗口滑动:
s -= arr[l]
:减去左边界元素,更新窗口和l += 1
:左指针右移,保持窗口长度为k
关键优化说明
- 避免浮点数运算:通过
s >= threshold * k
替代s/k >= threshold
,避免浮点数除法,提升效率并避免精度问题 - 固定窗口滑动:窗口长度始终为k,每次滑动仅需更新左边界元素,时间复杂度从O(n*k)优化到O(n)
- 和的重用:利用前一个窗口的和,通过加减操作快速计算新窗口和,无需重复累加
复杂度分析
- 时间复杂度:O(n),其中n为数组长度。每个元素被右指针访问1次,左指针最多移动n次,总操作次数为O(n)
- 空间复杂度:O(1),仅使用常数级额外空间存储指针和临时变量
示例详解
以输入 arr = [2,2,2,2,5,5,5,8], k = 3, threshold = 4
为例:
-
初始化:
l=0, ans=0, s=0
-
r=0,元素2:
s = 0 + 2 = 2
r+1=1 < 3
,跳过判断
-
r=1,元素2:
s = 2 + 2 = 4
r+1=2 < 3
,跳过判断
-
r=2,元素2:
s = 4 + 2 = 6
r+1=3 >= 3
,判断6 >= 4*3=12
?否,不计数s = 6 - 2 = 4
,l=1
-
r=3,元素2:
s = 4 + 2 = 6
r+1=4 >= 3
,判断6 >= 12
?否,不计数s = 6 - 2 = 4
,l=2
-
r=4,元素5:
s = 4 + 5 = 9
r+1=5 >= 3
,判断9 >= 12
?否,不计数s = 9 - 2 = 7
,l=3
-
r=5,元素5:
s = 7 + 5 = 12
r+1=6 >= 3
,判断12 >= 12
?是,ans=1
s = 12 - 2 = 10
,l=4
-
r=6,元素5:
s = 10 + 5 = 15
r+1=7 >= 3
,判断15 >= 12
?是,ans=2
s = 15 - 5 = 10
,l=5
-
r=7,元素8:
s = 10 + 8 = 18
r+1=8 >= 3
,判断18 >= 12
?是,ans=3
s = 18 - 5 = 13
,l=6
-
最终返回
3
,即有3个满足条件的子数组。
总结
该解法通过滑动窗口技术高效解决了定长子数组平均值统计问题,核心优势在于:
- 线性时间复杂度:双指针移动确保每个元素仅被处理两次,适用于大规模数据
- 整数运算优化:通过和与阈值倍数比较,避免浮点数运算带来的精度问题和性能损耗
- 空间高效:仅使用常数级额外空间,满足内存限制要求
此方法适用于所有"定长子数组统计"场景,如子数组和、子数组最大值等问题,是滑动窗口技术的经典应用。在实际编程中,通过避免浮点数运算可以提升代码的运行效率和稳定性。