zhyDaDa的个人站点

单调栈

目录
单调栈
问题背景
流程
原理分析


问题背景

单调栈要解决如下问题:

对于数组中每一个数, 希望能获知其左右两侧离ta最近的比ta大的数的下标

举例: 对于一个数组[1, 4, 2, 8, 5, 7], 希望获得下表:
| index | value | left greater | right greater |
| :—: | :—: | :———-: | :———–: |
| 0 | 1 | / | 1 -> 4 |
| 1 | 4 | / | 3 -> 8 |
| 2 | 2 | 1 -> 4 | 3 -> 8 |
| 3 | 8 | / | / |
| 4 | 5 | 3 -> 8 | 5 -> 7 |
| 5 | 7 | 3 -> 8 | / |

想要所有信息, 如果暴力那么每次都是整体遍历, 时间复杂度$O(n^2)$
单调栈结构能使得整体时间复杂度优化至$O(n)$, 那么单次就是常数级别了

流程

准备一个作为我们的单调栈
由于要求两侧大的数, 所以栈的单调性是从栈底到栈顶由大到小

若要求更小的数, 反一反即可

我们先假定数组中无重复元素

遍历数组, 对于每个数:
+ 如果符合单调性(当前数小于栈顶数), 压栈
+ 如果栈顶小于入栈数, 那么弹栈, 直到能压栈

一个数被弹出, 其信息即可生成:
+ 其左侧最近的比ta大的数, 就是压在ta下面的那个数
+ 其右侧最近的比ta大的数, 就是等着压栈的那个数

下面没数就说明左侧没有比ta大的数, 记为空

当然, 会出现所有数遍历完成后, 还有数留存于栈中
此时我们要清算剩余的数:
+ 依次弹出
+ 规则同上, 只是右侧的信息一律记为空

关于数组中有重复数据, 在搞清原理后自然能明白怎么做

原理分析

我们记一个数a被弹出时, 等着压栈的数叫b, a正下方的叫c

为什么ba右侧最近的较大的数?
根据单调栈的定义, 栈中的元素从栈底到栈顶是从大到小的
因此a的上方全是比ta小的, 第一个出现的比a大的数为了进栈会一直弹栈
因此b必定是右侧第一个比a大的数

Avatar photo
我是 zhyDaDa

前端/UI/交互/独立游戏/JPOP/电吉他/游戏配乐/网球/纸牌魔术

发表回复