滑动窗口最大值不能只用std::max_element,因其每次滑动需遍历k个元素,时间复杂度o(n×k),k接近n时退化为o(n²)易超时;而用std::deque存索引(非值),通过从队尾弹出小于nums[i]的元素、从队首弹出过期索引,维护单调递减的索引序列,使每次操作均摊o(1)。

滑动窗口最大值为什么不能只用 std::max_element
因为窗口每次滑动都要重新遍历,时间复杂度是 O(n * k),当 k 接近 n 时退化成 O(n²)。实际面试或高频数据流场景下会超时。用 std::deque 维护“可能成为最大值”的候选索引,能把均摊时间压到 O(1) 每次操作。
deque 存什么?索引还是值?
存数组下标(int),不是值本身。这样既能快速定位原数组元素,又能判断某个候选是否已滑出窗口。
- 存值会导致无法判断该值对应的位置是否还在当前窗口内
- 存索引可直接比较
deque.front() == i - k判断是否过期 - 所有比较逻辑都基于
nums[deque.back()]这类访问,避免额外 map 或 pair 开销
deque 的维护逻辑:从尾部弹出 + 从头部弹出
核心是两个动作:入队前清尾、出窗时清头。不是“维护单调递减”,而是“维护一个以值为关键字的单调递减索引序列”。
- 入队(新索引
i):while 非空且nums[deque.back()] → <code>deque.pop_back();然后deque.push_back(i) - 出窗:if
deque.front() == i - k→deque.pop_front()(注意是==,不是,每个索引只过期一次) - 窗口形成后(
i >= k - 1),nums[deque.front()]就是当前窗口最大值
示例片段(假设 nums = {1,3,-1,-3,5,3,6,7}, k = 3):
for (int i = 0; i = k - 1) result.push_back(nums[dq.front()]); }
边界和性能容易忽略的点
std::deque 的 pop_front() 和 pop_back() 是常数时间,但频繁调用仍比 vector 尾插略慢;真正影响性能的是误判过期条件或比较符号写反。
- 错误写法:
dq.front() —— 会导致合法索引被提前删掉 - 错误写法:
nums[dq.back()] (漏了等号)—— 相同值时旧索引没被剔除,后续窗口移动后可能返回过期位置 - 空输入必须处理:
if (nums.empty() || k == 0)直接返回空vector -
k == 1时 deque 几乎不剪枝,但逻辑仍正确;k > nums.size()应按题意返回空或单元素,需明确需求
真正难的不是写对循环,是在多组边界数据(比如全相同数、严格递减、k=1/k=n)下验证 deque 内部状态是否始终满足“front 是当前窗口最大值索引”这一不变量。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











