文章目录
理论基础
1. 什么时候用单调栈
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。
2. 原理
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是只需要遍历一次。
3. 注意事项
- 单调栈里存放的元素是什么?
单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。 - 单调栈里元素是递增呢? 还是递减呢?
要使用递增循序(再强调一下是指从栈头到栈底的顺序),因为只有递增的时候,加入一个元素i,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。 (针对题739)
4. 判断条件
使用单调栈主要有三个判断条件。
- 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
5. 题目类型总结
- 比当前元素更大的下一个元素
- 比当前元素更大的前一个元素
- 比当前元素更小的下一个元素
- 比当前元素更小的前一个元素
6.代码实现
使用Deque<>() stack = new LinkedList<>()
T739. 每日温度(单调递增)*
- 思路
遇到比栈顶元素小的,直接入栈;
遇到比栈顶元素大的,一直弹出栈顶元素,直到比栈顶元素小,即入栈。 - 代码
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int len = temperatures.length;
int[] res = new int[len];
Deque<Integer> stack = new LinkedList<>();//单调栈:从大到小,即先出栈的是小的
stack.push(0);
for(int i = 1;i<len;i++){
if(temperatures[i]<= temperatures[stack.peek()]){//小于就直接加
stack.push(i);
}else{
//大于就一直 给res赋值 直到小于
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){
res[stack.peek()] = i - stack.pop();
}
stack.push(i);
}
}
return res;
}
}
T496. 下一个更大元素 **
//上一题的变形,用nums2去构造栈,放入map里面,用nums1元素去搜索
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
HashMap<Integer,Integer> map = new HashMap<>();//nums2数字:下一个最大元素
Deque<Integer> stack = new LinkedList<>();//单调栈,依次减小
int[] res = new int[nums1.length];//存放结果
//操作数组2,建立一个单调栈
stack.push(nums2[0]);
for(int i=1;i<nums2.length;i++){
if(nums2[i] < stack.peek()){
stack.push(nums2[i]);
}else{
while(!stack.isEmpty() && nums2[i] > stack.peek() ){
map.put(stack.pop(),nums2[i]);
}
stack.push(nums2[i]);
}
}
while(!stack.isEmpty()){
map.put(stack.pop(),-1);
}
//由数组1的数字去找map里面的元素
for(int i=0;i<nums1.length;i++){
res[i] = map.get(nums1[i]);
}
return res;
}
}
T503. 下一个更大元素Ⅱ (循环数组) **
处理循环数组,把两个数组拼接在一起
class Solution {
public int[] nextGreaterElements(int[] nums) {
Deque<Integer> stack = new LinkedList<>();//数字:索引
int[] res = new int[nums.length];
Arrays.fill(res,-1);
for(int i=0;i<nums.length*2;i++){
while(!stack.isEmpty() && nums[i%nums.length] > nums[stack.peek()]){
res[stack.peek()] = nums[i%nums.length];
stack.pop();
}
stack.push(i%nums.length); //简化了之前的代码,无论大于还是小于,最终都要入栈
}
return res;
}
}
T42. 接雨水
class Solution {
//单调栈:从大到小
//分小于大于等于三种情况
public int trap(int[] height) {
int size = height.length;
if(size <= 2){return 0;}
int sum = 0;
Deque<Integer> stack = new LinkedList<>();//索引
stack.push(0);
for(int i=1;i<size;i++){
if(height[i]< height[stack.peek()]){
stack.push(i);//小于 入栈
}else if(height[i] == height[stack.peek()]){
stack.pop();
stack.push(i);//相等 左边的放不了水
}else{
while(!stack.isEmpty() && height[stack.peek()]<height[i]){
//由三根柱子决定接水量
int mid = stack.pop();//中间柱子索引
if(!stack.isEmpty()){
int left = stack.peek();//左边的索引
int h = Math.min(height[left],height[i])-height[mid];//高度
int w = i - left - 1;//宽度
int temp = h*w;
sum += temp;
}
}
stack.push(i);
}
}
return sum;
}
}
真题部分
腾讯1
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
//构造两个单调栈:分别是从左到右 右到左便利
//入栈前 栈的大小就是可以看到的元素
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int size = sc.nextInt();
int[] nums = new int[size];
for(int i=0;i<size;i++) {
nums[i] = sc.nextInt();
}
int[] left = new int[size];
int[] right = new int[size];
Deque<Integer> stackLeft = new LinkedList<>();
Deque<Integer> stackRight = new LinkedList<>();
stackLeft.push(nums[0]);
left[0] = 0;
stackRight.push(nums[size-1]);
right[size-1] = 0;
//从左看
for(int i=1;i<size;i++){
left[i] = stackLeft.size();
if(nums[i]<=stackLeft.peek()){
stackLeft.push(nums[i]);
}else{
while(!stackLeft.isEmpty() && stackLeft.peek() < nums[i]){
stackLeft.pop();
}
stackLeft.push(nums[i]);
}
}
//从右看
for(int i=size-2;i>=0;i--){
right[i] = stackRight.size();
if(nums[i]<=stackRight.peek()){
stackRight.push(nums[i]);
}else{
while(!stackRight.isEmpty() && stackRight.peek() < nums[i]){
stackRight.pop();
}
stackRight.push(nums[i]);
}
}
//左右求和
int[] res = new int[size];
for(int i=0;i<size;i++){
res[i] = left[i] + right[i]+1;
}
for(int i=0;i<size;i++){
System.out.print(res[i]+" ");
}
}
}