前言
单调栈,理解起来很简单,实现起来也不难,但是具体应用起来,还有点不容易。 刷leeCode的时候做到了好几个关于单调栈的题,简单总结一波,希望可以便人便己。
一、什么是单调栈?
首先单调栈是一种数据结构,更确切的说,它是一种栈。普通的栈结构,可以在任意时刻进行入栈、出栈(当然,入栈时要保证栈不满、出栈时栈不能空)。而对于单调栈来说,其需要保证栈内元素的有序性(递增、递减或者其他的规则),入栈和出栈操作需要受到一定的限制。
如下图所示:
二、单调栈用于何处?
参考【1】中的评论对于这点介绍的比较清晰。 摘录如下:
单调栈性质:
- 单调栈里的元素具有单调性。
- 递增(减)栈中可以找到元素左右两侧比自身小(大)的第一个元素。
我们主要使用第二条性质 (也就是说我们更关注的是栈元素入栈和出栈的时刻,而不是任意时刻栈中元素的有序性),该性质主要体现在栈调整过程中。下面以递增栈为例(假设所有元素都是唯一),当新元素入栈。
- 对于出栈元素来说:找到右侧第一个比自身小的元素。
- 对于新元素来说:等待所有破坏递增顺序的元素出栈后,找到左侧第一个比自身小的元素。
三、举例
下面简单举一个leecode中的例子进行说明。
3.1 题目描述
如下:
3.2 解法
如果有了上面单调栈的了解和铺垫,这题就很简单了。 求指定元素的右边第一个大的元素,和我们在第2节中说的类似,只不过为了我们得把递增栈换成递减栈。
代码如下:
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
vector<int> ans;
unorder_map<int,int> greatMap;
stack<int> greatSta;
nums2.push_back(INT_MAX);
for(int i = 0;i<nums2.size();++i){
while(greatSta.empty() == false){
int tmpTop = greatSta.top();
if(tmpTop<nums2[i]){
greatMap[tmpTop] = nums2[i];
greatSta.pop();
}else{
break;
}
}
greatSta.push(nums2[i]);
}
//收集
for(auto num:nums1){
if(greatMap.count(num) > 0){
if(greatMap[num] ==INT_MAX){
ans.push_back(-1);
}else{
ans.push_back(greatMap[num]);
}
}
}
return ans;
}
};
还有一些比较经典的单调栈的题型,这里就不说了。关键点就是从题目中分析出可以使用单调栈的性质出来。
三、总结
一般情况下,单调栈多用于一维序列之中。主要是以比较快的方式(O(1))找到指定元素左右两侧比他大(或小)的数的位置(一般情况下,我们都找第一个)。
具体对于某一个元素来说,入栈能确定与其左边元素(最接近的)的关系(递增栈是小于,递减栈是大于)。出栈可以确定与其右边元素(最接近)的关系(递增栈是小于,递减栈是大于)。
好了,基本就这些了,理论完了,还是实操一下比较好。
参考
【1】、刷题笔记6(浅谈单调栈)
【2】、496. 下一个更大元素 I