单调栈时间复杂度为O(n),因每个元素最多入栈出栈一次;暴力遍历为O(n²),易超时。栈存下标,维护递减序列,遇更大值即弹栈填答案,需注意初始化、边界判断与严格大于逻辑。

为什么用单调栈而不是暴力遍历
暴力遍历每个元素向右找第一个更大值,时间复杂度是 O(n²),遇到 10⁵ 级别数组就容易超时。单调栈的核心价值在于:**每个元素最多入栈、出栈各一次,整体 O(n) 时间搞定**。它利用“栈中元素从栈底到栈顶严格递减”这一性质,让尚未找到“下一个更大元素”的数暂存,等真正更大的数出现时批量处理。
实际写的时候,栈里存的不是值,而是下标(vector),方便回填结果数组;遇到比栈顶对应元素大的数,就弹栈并记录答案。
标准实现中栈内维护的是“递减下标”
栈里保持 nums[stk.top()] > nums[i] 成立,一旦 nums[i] > nums[stk.top()],说明 i 就是栈顶下标对应的“下一个更大元素”。
- 初始化空栈
stack stk 和结果数组 vector res(nums.size(), -1)
- 遍历
i 从 0 到 nums.size()-1,对每个 i:
- 当栈非空且
nums[i] > nums[stk.top()],弹出 top = stk.top(); stk.pop(); res[top] = nums[i];
- 把
i 压入栈
- 循环结束后,栈中剩余下标对应位置仍为
-1(右侧无更大元素)
stack stk 和结果数组 vector res(nums.size(), -1)
i 从 0 到 nums.size()-1,对每个 i:
- 当栈非空且
nums[i] > nums[stk.top()],弹出top = stk.top(); stk.pop(); res[top] = nums[i]; - 把
i压入栈
-1(右侧无更大元素)注意:不能在每次循环开头就压栈,必须先处理所有能被 nums[i] 解决的栈顶元素——否则顺序错乱,漏解。
遇到重复元素或相等情况怎么办
单调栈默认处理的是“下一个**更大**”(不包含等于)。如果题目要求“下一个**大于等于**”,只需把条件从 > 改成 >= 即可。
但要注意:若原数组有大量重复值(如全 1),用 >= 会导致栈几乎不弹出,退化成接近 O(n²) 行为(虽然仍是单次扫描,但常数变大)。实际中建议确认题干是否真需要“等于”,多数场景仍是严格大于。
另外,std::stack 默认底层是 deque,性能够用;若追求极致,可用 vector 手动模拟栈(push_back/pop_back),内存局部性更好。
边界与调试时最常踩的坑
- 忘记初始化
res 为 -1,导致未赋值位置是随机值(尤其用 int res[n] 的 C 风格写法)
- 比较时误写成
nums[i] > stk.top()(忘了取下标对应值)
- 弹栈后没更新
stk.top() 就继续用旧值,引发越界或逻辑错误(应放在 while 循环体内反复检查)
- 输入为空数组,
stk.top() 在空栈上调用——务必加 !stk.empty() 判断
res 为 -1,导致未赋值位置是随机值(尤其用 int res[n] 的 C 风格写法)nums[i] > stk.top()(忘了取下标对应值)stk.top() 就继续用旧值,引发越界或逻辑错误(应放在 while 循环体内反复检查)stk.top() 在空栈上调用——务必加 !stk.empty() 判断一个典型崩溃错误是:std::out_of_range 或访问非法内存,往往就卡在这几个点上。写完先用 [1]、[2,1]、[1,2,3,4] 这类小用例手跑一遍流程,比直接交 OJ 更快定位问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











