【算法图解】单调栈:解决 Next Greater 问题

单调栈用“维护单调性”把看似 O(n²) 的比较问题降到 O(n)。本文讲清楚单调递增/递减栈的含义、入栈出栈时机、典型题型(下一个更大元素、每日温度、柱状图最大矩形)与代码模板。

18 分钟阅读
小明

【算法图解】单调栈:解决 Next Greater 问题

你可能做过这种题:

  • 对于数组每个元素,找右边第一个比它大的元素(Next Greater Element)

朴素解法:

  • 对每个元素向右扫描

最坏 $O(n^2)$。

单调栈能把它降到 $O(n)$。

核心直觉:

你不需要把“所有历史元素”都记住,只需要记住“还有机会被未来元素解决”的那些。


1. 什么是单调栈?

单调栈维护一种单调性:

  • 单调递减栈:栈内从栈底到栈顶递减
  • 单调递增栈:栈内从栈底到栈顶递增

很多 Next Greater 类题用单调递减栈

  • 当你看到一个更大的元素时,它可以“解决”栈顶(以及一串更小的)。

2. Next Greater Element 模板(可运行)

function nextGreater(nums) {
  const res = new Array(nums.length).fill(-1)
  const st = [] // 存索引

  for (let i = 0; i < nums.length; i++) {
    while (st.length && nums[i] > nums[st[st.length - 1]]) {
      const j = st.pop()
      res[j] = nums[i]
    }
    st.push(i)
  }

  return res
}

console.log(nextGreater([2, 1, 2, 4, 3]))
// [4, 2, 4, -1, -1]

为什么是 O(n)?

  • 每个元素最多入栈一次、出栈一次

总操作次数是线性的。


3. 单调栈的两种常见形态

3.1 找右边第一个更大

  • 从左到右扫描
  • 用递减栈

3.2 找左边第一个更小/更大

  • 从左到右扫描
  • 用递增/递减栈(看题意)

也可以反过来从右到左。


4. 典型题:每日温度

题意:每一天要等几天才会更暖。

本质:对每个元素找右边第一个更大元素的“距离”。

function dailyTemperatures(t) {
  const res = new Array(t.length).fill(0)
  const st = []

  for (let i = 0; i < t.length; i++) {
    while (st.length && t[i] > t[st[st.length - 1]]) {
      const j = st.pop()
      res[j] = i - j
    }
    st.push(i)
  }

  return res
}

5. 典型题:柱状图最大矩形(思路版)

这题会用到“找每个柱子左右第一个更小的位置”。

单调递增栈可以在 O(n) 内求出:

  • 左边界
  • 右边界

然后面积就是:

  • height[i] * (right[i] - left[i] - 1)

(完整代码略长,但核心就是两个边界的单调栈模板。)


6. 面试回答模板

  • 我用单调栈维护一个递减(或递增)序列。
  • 当遇到一个元素打破单调性时,弹栈并结算答案。
  • 每个元素最多入栈出栈一次,因此时间 O(n),空间 O(n)。

总结

  • 单调栈把“向左/向右找第一个更大/更小”降到 O(n)。
  • 关键是:弹栈时结算,入栈保持单调。
  • 高频题:Next Greater、每日温度、柱状图最大矩形。