滑动窗口平均值用deque维护实时和,sum动态增减,未满窗时按实际大小求均值;最大值用单调递减索引队列,需校验队首索引是否在窗内。

滑动窗口平均值:用 std::deque 维护实时求和
直接用循环每次重算 O(n) 太慢,尤其窗口大、数据流快时。核心是复用上一窗口的和:sum -= old_value; sum += new_value;。但得记住窗口里每个值,std::deque 比 std::vector 更合适——头删尾插都是 O(1)。
常见错误是没处理窗口未填满的情况(比如刚启动时只有 2 个数,但窗口大小是 5),此时不该返回 NaN 或崩溃,而应按实际数量算平均值。
- 初始化一个空
std::deque<double></double>和double sum = 0.0 - 每来一个新值
x:sum += x;,deque.push_back(x); - 若
deque.size() > window_size,则sum -= deque.front(); deque.pop_front(); - 平均值就是
sum / deque.size()(不是固定除window_size)
滑动窗口最大值:单调队列必须存索引
用 std::deque<int></int> 存的是数组下标,不是值本身——否则无法判断某个最大值是否已滑出窗口。队列保持“从头到尾值递减”,同时确保队首索引始终在当前窗口内。
容易踩的坑是只比较值、忽略索引有效性。比如窗口 [i, i+k-1],队首索引 idx 若 ,就得 <code>pop_front;插入新值前,从队尾开始弹出所有 ≤ 当前值的元素,保证单调性。
立即学习“C++免费学习笔记(深入)”;
- 队列里存的是原始数据的下标,对应值通过
arr[idx]取 - 每次滑动后,先清理过期索引:
while (!dq.empty() && dq.front() - 再清理小值:
while (!dq.empty() && arr[dq.back()] - 队首就是当前窗口最大值的下标
多指标复用同一窗口:别为每个指标单独存一份数据
平均值要和,最大值要单调队列,最小值是另一个单调队列,标准差还得维护平方和……全存下来内存翻倍、逻辑耦合。更合理的方式是共享输入流 + 独立状态,用结构体封装:
struct SlidingWindowStats { std::deque<double> values; double sum = 0.0; std::deque<size_t> max_deque; // 存索引 std::deque<size_t> min_deque; double sum_sq = 0.0; };
关键点:所有操作基于同一份 values 的增删,但各指标更新时机一致(比如都在 add() 里触发),避免因调用顺序不同导致状态错位。别把 max_deque 和 min_deque 更新写在不同函数里。
性能陷阱:频繁拷贝、浮点精度、整数溢出
窗口很大时,std::deque 内部是分段连续内存,迭代器失效规则比 std::vector 复杂,但只要不用随机访问中间元素,影响不大。真正拖慢的是无意识的拷贝——比如把整个 deque 传给函数时用了值传递。
另外,累加和用 double 能扛住大部分场景,但如果数据是 int64_t 且窗口超 1e6,sum 可能丢失低比特精度;而用 int64_t 累加又容易溢出。折中方案是:整数输入 → 用 long double 累加和与平方和,最后转成所需类型输出。
- 所有接口参数优先用
const T&或指针,避免std::deque<t></t>值传 - 不要用
std::accumulate在运行时反复遍历窗口,它破坏 O(1) 更新前提 - 如果业务允许误差,可对超大窗口启用采样或指数衰减近似,但这是另一回事了
窗口越长,状态同步的边界条件越多;最常漏掉的是“窗口清空后重新填充”那段过渡期逻辑——这时候 sum 是 0,但 deque 已空,size() 是 0,除零风险就藏在这儿。