单调栈是用列表模拟并维护元素单调性的技巧,核心解决“每个元素右侧第一个更大值”问题,时间复杂度o(n);栈存索引,栈底到栈顶严格递减,入栈前弹出破坏单调性的元素,通过nums[i] > nums[stack[-1]]判断,需先判栈非空再取值。

单调栈不是内置结构,而是一种用列表模拟栈、并主动维护元素单调性的技巧。核心目标是高效解决“每个元素右侧第一个更大值”这类问题,时间复杂度稳定在 O(n),远优于暴力遍历的 O(n²)。
关键逻辑:栈中存索引,入栈前弹出破坏单调性的元素
栈底到栈顶保持严格递减(找“下一个更大”时),这样一旦遇到比栈顶大的数,就能立刻确认栈顶元素的答案就是当前数。必须存原始索引而非数值——否则无法回填结果数组对应位置。
- 每次比较用 nums[i] > nums[stack[-1]],不是和索引值比
- while 条件顺序不能颠倒:stack and nums[i] > nums[stack[-1]],先判非空再取值,避免 IndexError
- 每个索引最多入栈、出栈各一次,保证线性时间
标准实现:一次遍历完成所有更新
以数组 [1,3,4,2] 为例:
- i=0,栈空,push(0) → 栈=[0]
- i=1,nums[1]=3 > nums[0]=1,pop(0),result[0]=3;再 push(1) → 栈=[1]
- i=2,nums[2]=4 > nums[1]=3,pop(1),result[1]=4;push(2) → 栈=[2]
- i=3,nums[3]=2
- 遍历结束,栈中剩余索引对应位置保持 -1(无更大值)
处理循环数组:用取模代替拼接
面对如 [1,2,1] 这类循环数组,最后一个 1 的答案是 2(绕回开头)。不必复制数组浪费空间,只需遍历 2n−1 次,真实索引用 i % n 计算。
- 初始化 result = [-1] * n,利用 -1 作为未更新标记,防止重复赋值
- 内层 while 中仍用原始数组下标比较:nums[idx] > nums[stack[-1]]
- 栈中始终存原始索引(0 到 n−1),确保 result 更新位置准确
常见误区提醒
容易写错但影响巨大的细节:
- 只 append 不 pop → 栈不断膨胀,失去单调性,退化为普通容器
- 弹出条件写反,比如写成 nums[i] → 变成维护递增栈,逻辑完全错位
- 用栈存数值而非索引 → 找不到该填到 result 哪个位置,无法构造输出
- 忽略空栈判断,直接访问 stack[-1] → 运行时报错











