面试知识库

单调栈模板:一眼识别“下一个更大元素”类问题#

很多同学第一次看到单调栈时会觉得它很神秘,其实它的本质非常朴素:

用一个“有序”的栈,帮我们快速找到某个元素左边或右边第一个更大 / 更小的位置。

只要题目出现“下一个更大元素”“下一个更小元素”“最近更大”“最近更小”“以当前元素为高/低点扩展区间”,单调栈基本就该上场了。

⚡ 速记版模板#

// 模板用途:在线性扫描中,快速确定元素的“最近更大/更小”位置
Deque<Integer> stack = new ArrayDeque<>(); // 单调栈:通常存下标,便于算距离和定位答案
for (int index = 0; index < nums.length; index++) { // 线性扫描每个元素
    while (!stack.isEmpty() && nums[index] > nums[stack.peek()]) { // 当前值更大,说明栈顶元素“右侧第一个更大值”已出现
        int prevIndex = stack.pop(); // 取出等待结算的下标
        answer[prevIndex] = index; // 记录其答案(这里记录位置;有些题记录值或距离)
    }
    stack.push(index); // 当前下标入栈,等待未来更大元素来结算
}
java

🎯 什么时候想到单调栈?#

典型信号:

  • 问右侧第一个更大 / 更小元素
  • 问左侧第一个更大 / 更小元素
  • 问每个元素作为最小值或最大值时的影响范围
  • 需要把暴力的“向两边扩展”优化掉

Hot 100 里的典型题目:

💡 核心思路#

单调栈通常存的是 下标,不是值本身。这样可以同时拿到:

  • 当前元素值:nums[index]
  • 元素位置
  • 两个位置之间的距离

常见两种栈:

  1. 单调递减栈

    • 栈顶到栈底递减
    • 常用于找“下一个更大元素”
  2. 单调递增栈

    • 栈顶到栈底递增
    • 常用于找“下一个更小元素”

🚀 模板一:找右侧第一个更大元素#

这个模板里:

  • 栈里维护一个递减序列
  • 当前元素一旦更大,就可以结算栈顶元素的答案

🚀 模板二:找左右边界#

很多题并不直接问“下一个更大元素是谁”,而是问当前元素能向左右扩多远。

适用场景:

  • 柱状图最大矩形
  • 子数组最小值 / 最大值贡献问题
  • 需要知道某个元素的影响范围

🚀 模板三:单调栈 + 面积计算#

以柱状图最大矩形为代表:

这里额外加一个结尾高度 0,是为了把栈里剩下的柱子统一结算干净。

🧠 常见题目怎么套?#

1. 每日温度#

  • 维护一个递减栈
  • 当前温度更高时,栈顶那天就找到了答案
  • 答案是下标差值

2. 柱状图最大矩形#

  • 维护一个递增栈
  • 当前高度更小时,说明某些柱子的右边界确定了
  • 计算以出栈柱子为高的最大面积

3. 接雨水#

这题可用双指针,也可用单调栈:

  • 当前柱子更高时,可以和栈里的“凹槽”形成积水区间

⚠️ 易错点#

  1. 栈里存值还是下标

    • 绝大多数题都建议存下标
  2. 大小关系写错

    • 是 > 还是 >=
    • 是 < 还是 <=
    • 取决于你想保留相等元素还是合并相等元素
  3. 最后一批元素没结算

    • 柱状图类题常常要补一个哨兵 0
  4. 宽度公式容易错

    • 通常是 right - left - 1

🎨 面试时怎么说#

你可以这样说:

我用单调栈维护一组还没有确定答案的下标,一旦当前元素破坏了栈的单调性,就说明栈顶元素的右侧边界已经找到了,可以立刻结算答案。

📌 一句话总结#

单调栈的核心不是栈,而是:

  • 帮你维护一个“还没结算答案”的候选集合
  • 一旦出现更大 / 更小元素,就批量结算