单调栈

​ 单调栈是保持栈元素是有序的,如果破坏了单调性则出栈,直到满足栈的单调性。

​ 一般单调栈分为单调递增栈和单调递减栈。

​ 其主要解决:找到下一个比它大的元素或者比它小的元素

核心原理

当你入栈的时候,发现栈顶元素比你大,那么就让栈顶元素出栈,直到遇到比你小的元素,你再入栈。

从上面的原理你可以看出,单调递增栈可以找到下一个比它小的元素

例子:

data = [1,3,5,1,2,3,6]

单调栈一般存储的内容是数据在原始数组中的具体位置,也就是索引。下面为了方便介绍单调栈的单调性,选择存储元素的具体值进行介绍。

  • 第一步入栈 stack = [1]
  • 判断3是否比栈顶元素小,如果小,则让栈顶元素出栈,让3进栈,如果比栈顶元素大,那么直接进栈就可以了 此时 stack=[1,3]
  • 与第二步一样 此时stack=[1,3,5]
  • 当1比5小的时候,5就找到了比它小的元素了,然后就可以处理具体的逻辑了 5出栈,然后继续判断1是否小于3,直到遇到不小于1的元素,1才入队 此时stack=[1,1]
  • 之后2,3,5都比栈顶元素大,所以依次入队 此时stack=[1,1,2,3,6]
  • 也就是说这些元素没有找到序列中比它小的元素。
  • 在上面的逻辑中,我们发现单调栈处理数据时,数据每个元素的位置是固定的。

为什么单调栈一般存储的是元素的位置,而不是值呢?

主要原因是在数组中的元素的值是可重复的,但是索引是唯一的。

如果我们要计算下一个比它小的元素的距离时,我们只需要将该元素的索引 - 栈顶元素的索引就可以直接计算出距离了。

接下来我们使用具体的代码来解决相应的问题。

给定一个数组,然后找到每一个元素右边比它小的元素之间的距离。

package monotonicincreasingstack

func GetDistances(data []int) []int {
	n := len(data)
	if n == 0 {
		return []int{}
	}

	result := make([]int, len(data))

	stack := []int{}

	for i := 0; i < n; i++ {

		for len(stack) > 0 && data[stack[len(stack)-1]] /*栈顶元素*/ > data[i] {
			topIdx := stack[len(stack)-1]
			stack = stack[:len(stack)-1] //出栈
			result[topIdx] = i - topIdx
		}

		stack = append(stack, i)

	}

	return result
}

单调递减栈与单调递增栈逻辑基本上一致,只是在判断出队的时候,时采用栈顶元素小于当前元素的时候出栈。

解决下一个比它大的元素。

应用

1. 基础入门(下一个更大/更小元素)

这些题目是单调栈的模板题,适合用来熟悉“入栈、出栈、更新结果”的标准流程。

题号 题目名称 (中文/英文) 难度 关键点
496 查看下一个更大元素 简单 基础模板,结合哈希表存储映射。
503 下一个更大元素II 中等 循环数组处理(遍历两遍 + 取模 %n)。
739 每日温度 中等 计算距离i - stack.top())。
1475 商品折扣后的最终价格 简单 找右侧第一个更小或相等的元素。

2. 进阶应用(贡献法与区间最值)

这类题目不再是简单的找邻居,而是利用单调栈确定的边界来计算面积、和或宽度。

题号 题目名称 (中文/英文) 难度 关键点
84 柱状图中的最大矩形 (Largest Rectangle in Histogram) 困难 经典必做。找左右两侧第一个更小的元素确定宽度。
85 最大矩形 (Maximal Rectangle) 困难 84 题的二维变种,逐行转换成柱状图处理。
42 接雨水 (Trapping Rain Water) 困难 找左右两侧第一个更大的元素,计算“凹槽”面积。
907 子数组的最小值之和 (Sum of Subarray Minimums) 中等 利用单调栈找每个元素作为最小值的作用范围
1856 子数组最小乘积的最大值 中等 结合前缀和与单调栈边界计算。

3. 字符串与序列优化(贪心 + 单调栈)

这类题目通常要求我们在移除若干字符后,使得剩下的序列字典序最小或最大。

题号 题目名称 (中文/英文) 难度 关键点
402 移掉K位数字 中等 贪心思想:若后一个数字更小,则删掉当前大的。
316 / 1081 去除重复字母 (Remove Duplicate Letters) 中等 保证字典序最小且每个字符只出现一次。
321 拼接最大数 (Create Maximum Number) 困难 单调栈提取序列 + 序列合并。

4. 树与表达式处理

单调栈有时也会出现在处理树结构或特定数学表达式中。

题号 题目名称 (中文/英文) 难度 关键点
654 最大二叉树 (Maximum Binary Tree) 中等 构建笛卡尔树,单调栈可以在 $O(n)$ 时间完成。
255 验证二叉搜索树的前序遍历 中等 使用单调栈模拟树的遍历过程(会员题)。
962 最大宽度坡 (Maximum Width Ramp) 中等 寻找 $A[i] \leq A[j]$ 的最大 $j - i$。