何为单调栈?顾名思义,单调栈即满足单调性的栈结构,可以是单调递增或单调递减。在单调栈中,栈顶元素是栈内最大(或最小)的元素,而栈底元素是栈内最小(或最大)的元素。
单调栈在解决一些与区间相关的问题时非常有用 。它可以帮助我们快速找到每个元素左边或右边第一个比它大或小的元素:通过维护一个单调递增栈,我们可以找到每个元素右边第一个比它小的元素;通过维护一个单调递减栈,我们可以找到每个元素右边第一个比它大的元素。
单调栈(monotonic stack)是一种特殊的栈数据结构,常用于解决与区间相关的问题。它的特点是栈内元素具有单调性,可以是单调递增或单调递减。
这里的「单调」为广义上的单调,即为非严格单调性,包括「等于」的情况。
单调栈支持以下操作:
单调栈在解决一些与区间相关的问题时非常有用 ,例如:
单调栈的基本思想是维护一个单调递增或单调递减的栈。具体操作如下:
对于数组 [2,1,5,6,2,3],我们维护一个单调递增栈(栈底元素最小、栈顶元素最大)的过程如下:
第一步,栈为空,入栈新元素 2,此时单调递增栈(左侧为栈底、右侧为栈顶)的状态为:
1 | monoStack: [2] |
第二步,栈不为空,新元素 1 小于栈顶元素 2,弹出元素 2 后,栈为空,入栈新元素 1:
1 | monoStack: [1] |
第三步,栈不为空,新元素 5 大于栈顶元素 1,入栈新元素 5;栈不为空,新元素 6 大于栈顶元素 5,入栈新元素 6:
1 | monoStack: [1, 5, 6] |
第四步,栈不为空,新元素 2 小于栈顶元素 6,栈顶元素 6 出栈;栈不为空,新元素 2 小于栈顶元素 5,栈顶元素 5 出栈:
1 | monoStack: [1] |
第五步,栈不为空,新元素 2 大于栈顶元素 1,新元素入栈后满足单调性,新元素 2 入栈:
1 | monoStack: [1, 2] |
第六步,栈不为空,新元素 3 大于栈顶元素 2,新元素入栈后满足单调性,新元素 3 入栈:
1 | monoStack: [1, 2, 3] |
由第二步可知,元素
2右侧第一个比它小的元素是元素1;由第四步可知,元素5和元素6右侧第一个比它小的元素都是元素2;由最后一步可知,元素1,2,3右侧没有比它小的元素。
单调递增栈的栈顶元素最大,新插入的元素要不小于栈顶元素,否则只能把栈顶元素一个一个地弹出栈,直到满足为止。
1 | // insert x |
单调递减栈的栈顶元素最小,新插入的元素要不大于栈顶元素,否则只能把栈顶元素一个一个地弹出栈,直到满足为止。
1 | // insert x |
每个元素最多只会进栈、出栈一次,故时间复杂度为 O(n),空间复杂度为 O(n),n 为元素数量。
题目:寻找数组中每个元素右边比它小的第一个元素。
这可以使用单调递增栈找到每个元素右边第一个比它小的元素。
1 |
|
1 | 2 next smaller: 1 |
这个结果与上面的“维护单调栈示例过程”中的举例的结果一致。
在代码实现中,我们并没有直接在单调栈 monoStack 中存储数组元素的值,而是存储了数组元素对应的索引:
res 的对应位置,填充右边第一个比它小的值;nums 的索引对应的值不会改变,我们依然可以通过 monoStack 中维护的索引,获取对应的值,保证栈的单调性。