题目

给定一个数组,求每个位置右侧第一个比它大的元素。如果不存在,返回 -1

题目分析

如果每个位置都往右扫一次,时间复杂度就是 O(n^2)。真正的优化点在于:一旦某个元素已经被“更大值”盖过去了,它就没必要再参与后续比较。

单调栈的价值就在这里,它能帮我们高效维护“还没找到答案”的元素。

方法一:直觉解法

最朴素的办法是双重循环:

  • 外层枚举每个位置
  • 内层向右找第一个更大值

这种方法简单,但一旦数据量上来就很慢。

方法二:优化思路

我们维护一个“还没有找到答案”的下标栈,并让这些下标对应的值保持单调递减。

当遇到一个更大的新元素时:

  • 如果栈顶比它小,说明栈顶的答案已经出现
  • 不断弹栈,直到栈为空或者栈顶比它大

Go 实现

 1func nextGreater(nums []int) []int {
 2    ans := make([]int, len(nums))
 3    for i := range ans {
 4        ans[i] = -1
 5    }
 6
 7    stack := make([]int, 0, len(nums))
 8    for i, num := range nums {
 9        for len(stack) > 0 && nums[stack[len(stack)-1]] < num {
10            top := stack[len(stack)-1]
11            stack = stack[:len(stack)-1]
12            ans[top] = num
13        }
14        stack = append(stack, i)
15    }
16
17    return ans
18}

复杂度分析

  • 时间复杂度:O(n),每个元素最多入栈出栈一次
  • 空间复杂度:O(n)

易错点

  • 结果数组要先初始化为 -1
  • 栈里最好存下标,不要直接存值
  • 写代码前先想清楚你维护的是单调递增栈还是单调递减栈

我的复盘

单调栈最容易让人困惑的地方不是代码,而是“为什么它能做到不回头”。这次真正想清楚后,我觉得关键就在于:被弹出的元素,其答案已经确定了。

关联题

  • 每日温度
  • 接雨水
  • 柱状图中最大的矩形