顾名思义,就是栈中元素,按递增顺序或者递减顺序排列的时候
单调栈的本质就是栈,栈本身就是一种受限的数据结构。其受限指的是只能在一端进行操作。而单调栈在栈的基础上进一步受限,即要求栈中元素始终保持单调性
由于栈中都是单调的,因此其天生适合解决在其之后第一个小于(或大于)其本身的位置的题目
如果遇到题目需要找在其之后第一个小于(或大于)其本身的位置的题目,就可以考虑使用单调栈
优势:时间复杂度是线性的,每个元素遍历一次
套路:几乎都是从左到右或者从右到左遍历数组
核心:一些问题在遍历过程中,在更后面的位置出现了某个数,那么如果前面出现了比这个数更小(更大)的数,那么前面出现的这些数就对后面的答案求解一点作用都没有。因此为了降低程序的时间复杂度,我们只需要维护一个有序的单调栈就可以了
单调栈的代码一般都很短
这类题目考察什么知识点?
主要考察对于基础数据结构,特别是性质的理解,还有lazy addition的数据结构,对于某些add,remove我们延迟处理
frequency作为key,快速地找到出现次数最多的元素
更复杂的题目还用到了类似treeMap,LRU的知识来实现stack
反向模板三步走:
维持递增栈(或递减栈,取决于题目),栈顶向栈底
将栈顶元素放入result
当前iterate元素放入栈(可以是实际元素value,也可以只是index)
模板
//反向模板,可以只保存数字,因为iterator point就是当前位置
public int[] nextGreaterElements1(int[] nums){
int n = nums.length,res[] = new int[n];
Stack<Integer> stack = new Stack<>();
for (int i = n - 1;i >= 0;i --){
while(!stack.isEmpty() && nums[i] >= stack.peek()) stack.pop();
res[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i]);
}
return res;
}
//正向模板,因为需要post process,需要保存之前element的index
public int[] nextGreaterElement2(int[] nums){
int n = nums.length,res[] = new int[n];
Stack<Integer> stack = new Stack<>();
Arrays.fill(res, val:-1);
for (int i = 0;i < n;i ++){
while(!stack.isEmpty() && nums[stack.peek()] < nums[i])
res[stack.pop()] = nums[i];
stack.push(i);
}
return res;
}