当前位置: 首页 > news >正文

Swift 实战:用队列巧解 LeetCode 346 数据流中的移动平均数

在这里插入图片描述
在这里插入图片描述

文章目录

    • 前言
    • 描述
      • 题目示例:
    • 解决方案
    • 分析问题和答案
      • 代码拆解:
      • 为什么不用每次都去 for 循环 sum?
    • 示例测试和结果
      • 结果:
    • 时间复杂度
    • 控件复杂度
    • 总结

前言

在数据流处理中,实时计算“滑动窗口内的平均数”是一个非常常见的需求。不论是金融领域的股票价格走势,还是物联网设备的实时数据监控,移动平均(Moving Average) 都是核心操作。

LeetCode 第 346 题“数据流中的移动平均数”就是一道经典的滑动窗口题目,今天我们用 Swift 带大家一步步拆解这道题,写出一个简单又实用的 Demo,顺带聊聊它在实际开发中的应用场景。

描述

题目要求我们设计一个类 MovingAverage,它接受一个整数 size 作为滑动窗口的大小。每次调用 next(val: Int) 方法时,返回最近 size 个数据的平均值。如果数据量不足 size 个,那就用已有的数据来算平均。

题目示例:

输入:
["MovingAverage", "next", "next", "next", "next"]
[[3], [1], [10], [3], [5]]输出:
[null, 1.0, 5.5, 4.66667, 6.0]

简单来说:

  1. 初始化窗口 size = 3。
  2. 数据流依次输入 1、10、3、5。
  3. 每次调用 next() 方法都返回窗口内的平均值。

解决方案

我们可以用 队列(Queue)+ 滚动和(rolling sum) 的方式高效解决:

  • 队列用来存储滑动窗口中的元素。
  • 一个变量 sum 用来记录当前窗口内元素的总和。
  • 每次输入新数据后,如果队列长度超过 size,就把最旧的数据踢出去。

Swift 中没有内置的 Queue,我们可以用 Array + removeFirst() 来模拟。

分析问题和答案

直接上代码,后面我们一行行讲解:

class MovingAverage {private var windowSize: Intprivate var windowQueue: [Int]private var currentSum: Intinit(_ size: Int) {self.windowSize = sizeself.windowQueue = []self.currentSum = 0}func next(_ val: Int) -> Double {windowQueue.append(val)currentSum += valif windowQueue.count > windowSize {let removed = windowQueue.removeFirst()currentSum -= removed}return Double(currentSum) / Double(windowQueue.count)}
}

代码拆解:

  1. 属性定义:

    • windowSize: 滑动窗口的大小。
    • windowQueue: 模拟队列,记录窗口内的数据。
    • currentSum: 记录当前窗口内元素的总和。
  2. 初始化函数 init

    • 初始化窗口大小,并将队列与总和归零。
  3. 核心函数 next(val: Int)

    • 将新元素加入队列并更新总和。
    • 判断队列是否超出窗口大小,如果是,就把最早的元素从队列中移除,并从总和中减掉。
    • 返回当前窗口内的平均值。

为什么不用每次都去 for 循环 sum?

因为我们用 currentSum 滚动维护了窗口内的总和,每次加入或移除元素时只做一次加减运算,性能是 O(1),非常高效。

示例测试和结果

来跑一组示例看看效果:

let movingAverage = MovingAverage(3)
print(movingAverage.next(1))  // 输出: 1.0
print(movingAverage.next(10)) // 输出: 5.5
print(movingAverage.next(3))  // 输出: 4.6666666667
print(movingAverage.next(5))  // 输出: 6.0

结果:

1.0
5.5
4.6666666667
6.0

解释:

  1. 输入 1 -> 窗口:[1] -> 平均值 1.0
  2. 输入 10 -> 窗口:[1, 10] -> 平均值 (1+10)/2 = 5.5
  3. 输入 3 -> 窗口:[1, 10, 3] -> 平均值 (1+10+3)/3 = 4.6667
  4. 输入 5 -> 窗口:[10, 3, 5] -> 平均值 (10+3+5)/3 = 6.0

时间复杂度

  • 每次调用 next 方法的时间复杂度是 O(1),因为:

    • appendremoveFirst 是 O(1) 操作。
    • 维护 sum 的加减是 O(1)。
  • 整体时间复杂度是 O(n),n 为调用次数。

控件复杂度

  • 队列最多存储 size 个元素。
  • 空间复杂度是 O(size)

总结

这道题看似简单,但“实时数据滑动窗口统计”在很多实际场景都非常重要,比如:

  • 股票价格的 N 日均线。
  • IoT 设备的实时数据平滑。
  • 实时监控系统中的波动过滤。

本题的关键点就在于用一个“队列 + 滚动和”来高效实现 O(1) 的移动平均计算,避免每次都去重复累加。实现逻辑虽然简单,但设计思路却非常有实战意义。

http://www.dtcms.com/a/316736.html

相关文章:

  • 服务器端口连通性的测试工具和方法
  • XXL-JOB调度中心、执行器、Job之间的关系
  • MQTT:消息详解
  • 备忘录记事本 任务清单 html
  • 基于铁头山羊STM32的平衡车电机转速开环闭环matlab仿真
  • 线性规划最优解
  • 饿了么招java开发咯
  • tarjan找SCC,缩点建DAG,找唯一源头节点
  • 强光干扰下误报率↓82%!陌讯多模态融合算法在火焰识别的落地优化
  • 不可变集合
  • nflsoi 7.31 题解
  • 信息化项目验收,项目成功的最后确认
  • Redis中的sdshdr的len和alloc那块的知识点详解
  • 【经验记录贴】在windows系统中启动服务
  • CMU-15445(7)——PROJECT#2-BPlusTree-Task#2Task#3
  • BGA 芯片贴片加工关键注意事项
  • Fiddler 中文版实战指南,如何构建高效的 API 调试工作流?
  • 第13届蓝桥杯Scratch_选拔赛_真题2021年11月27日
  • 老旧体育场馆照明翻新:预算有限?3 步实现 “低成本升级”
  • 在具身智能火热加持下,看 2025 年机器人学术年会中的热点主题。PNP机器人展示力控、灵巧手捕捉等案例。
  • 利用链上数据进行数字资产量化因子发现
  • 计划任务:被忽视的网络与系统安全边界
  • Linux-Day10.系统安全保护web服务管理
  • 【springcloud的配置文件不生效】
  • Linux系统:基础I/O
  • 【RK3576】【Android14】Uboot下fastboot命令支持
  • 闸机控制系统从设计到实现全解析:第 4 篇:Redis 缓存与分布式锁实现
  • JavaScript 概述
  • Linux 逻辑卷管理:LVM 原理与 Stratis、VDO 特性对比
  • Vue2博客项目笔记(第一天)