单调栈
单调栈是一种特殊的栈,其栈内元素共有4种情况:单调增
、减
、不增
、不减
。
其常与一个经典问题相联系使用:专注于一个数组中的第
i
i
i 个元素,找左右两边第一个小于
、大于
、不小于
、不大于
的元素下标和值。
当需要找左边第一个满足条件的元素时,需要正向遍历。右边则需要反向遍历。
以一个常见的问题举例:柱状图中最大的矩形
看到问题首先进行分析,面积由长 * 宽两部分计算,而宽显然是
h
[
i
]
h[i]
h[i],长也显然是两端能延伸的最大长度。很容易想到这与找一个元素左右第一个小于的下标等价。
下面给出代码中单调栈的实现及解释:
# 仅为部分代码,只实现了找左侧第一个小于的元素
# 举的例子中,一律默认 栈底-[栈]-栈顶
stack<int>stk(n); # 单调栈,栈中存储的是下标,值可以通过h[下标]来访问
vector<int>pre(n); # pre[i] 代表 第i个元素 左侧的第一个满足要求的元素下标
for(int i=0;i<n;i++){
while(stk.size() and !(h[stk.top()] < h[i])){
# 此处判断的是,新来的元素若要加入时,栈不满足单调性,则需要不断弹出其中的元素。
# 直至加入后可以满足原来的单调性为止
# 举个例子: [1,2,4,5]
# 若新来的元素为3,加入后[1,2,4,5,3]显然不满足原来的单调增
# 于是不断弹出元素 直至为 [1,2]时,3加入才能满足单调性
# 当单调栈非空 且 栈顶代表的元素值 不小于 当前要新加入的元素时
stk.pop();
}
# 更新pre数组,若单调栈此时不为空,则栈顶一定是左侧第一个小于的元素
# 要取满足题意的,只需要取栈顶下标的右边一位即可
pre[i] = stk.size() ? stk.top() + 1 : 0;
# 然后把元素入栈
stk.push(i);
}