当前位置: 首页 > 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.lryc.cn/news/611123.html

相关文章:

  • 【RabbitMQ】高级特性—持久性、重试机制详解
  • 栈的输入与输出方式
  • 《算法导论》第 4 章 - 分治策略
  • Python Day23程序、进程、线程及多线程实现全解析 例题分析
  • 星图云开发者平台赋能商储油安全管控数字化转型
  • 为什么要选择时序数据库IoTDB?
  • Python爬虫08_Requests聚焦批量爬取图片
  • Pandas 入门:数据分析的得力工具
  • 嵌入式硬件中运放内部底层分析
  • 基于深度学习的医学图像分析:使用CycleGAN实现医学图像风格转换
  • 后量子时代已至?中国量子加密技术突破与网络安全新基建
  • 关于npx react-native run-android下载进程缓慢以及进程卡壳等问题的解决方案。
  • Java 大视界 -- Java 大数据在智能医疗电子病历数据分析与临床决策支持中的应用(382)
  • iOS混淆工具有哪些?技术演进与选型趋势全景解析
  • 企业如何用现代数仓架构挖掘新业务盈利点?AllData产品从目标、路径、结果给出答案
  • Go语言实战案例:使用sync.Mutex实现资源加锁
  • 查看 Redis 某个数据库的内存占用
  • 【前端】网站favicon图标制作
  • 力扣-208.实现Trie(前缀树)
  • C++ - 仿 RabbitMQ 实现消息队列--服务端核心模块实现(六)
  • Linux-Day11.WEB服务,虚拟主机
  • VUE丢失long类型精度,使用 json-bigint 库解析大整数
  • 人工智能领域、图欧科技、IMYAI智能助手2025年7月更新月报
  • 暑期算法训练.14
  • 关于如何SecureCRT软件连接开发板后默认显示大字体,且重启开发板或重新连接时不会重置的方法
  • Android原生项目集成Flutter模块极简指南
  • Linux学习-数据结构(链表)
  • 深入浅出:Ajax 与 Servlet 实现前后端数据交互
  • 01-数据结构
  • ES(Elasticsearch)进程掉线(节点脱离集群)问题