栈
使用范围
- 栈的特点就是“先入后出”,在题目处理过程中需要将数据存储在先入后出的这中容器中可以考虑用栈这种数据结构。
- 在问题中出现寻找左边/右边第一个大于/小于当前元素的数时,要想到用单调栈来处理问题,一般情况下单调栈问题也可以利用动态规划+双指针来处理。
这里列出典型的题目:
入门:后缀表达式或者逆波兰表达式
解题思路:
这一题还是比较简单的。求解栈的问题 关键是要判断何时入栈、何时出栈。
出现了运算符,将栈内的两个元素弹出,并进行相应的运算,如果不是就往栈里添加元素。
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<Integer>();
for(String token : tokens){
if("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token)){
// 注意 在Java中'' 和 “” 的区别
// '' 单引号里面的是字符 是char类型
// “” 双引号里面的是字符串 是String类型。 这里是String类型。
int nums1 = stack.pop();
int nums2 = stack.pop();
if("+".equals(token)){
stack.push(nums2 + nums1);
}
else if("-".equals(token)){
stack.push(nums2 - nums1);
}else if("*".equals(token)){
stack.push(nums2 * nums1);
}else if("/".equals(token)){
stack.push(nums2 / nums1);
}
}
else{
stack.push(Integer.valueOf(token)); // 字符串转换为int整型
}
}
return stack.peek(); // 注意 最后返回 stack.peek()
}
}
单调栈入门:每日温度
解题思路
找到右边比他大的第一个元素,并在当前下表记录与之下标差值(几天之后)
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
// 用到了先入后出
// 注意,可以将下表存入栈中,
// 1、可以计算距离
// 2、可以结合数组将对对应下标的距离放到数组中
Stack<Integer> stack = new Stack<>();
int[] result = new int[temperatures.length];
for(int i = 0;i<temperatures.length;i++){
while(!stack.empty() && temperatures[stack.peek()] < temperatures[i] ){
result[stack.peek()] = i - stack.peek();
stack.pop();
}
stack.push(i);
}
return result;
}
}
单调栈困难:接雨水
class Solution {
public int trap(int[] height) {
// 动态规划的解法
// 两个数组:leftMax 存储i位置 左边最大的元素,保存到i位置上
// rightMax 存储i位置 右边最大元素,保存到i位置上
// 得到每个位置的min(leftMax, rightMax) 在减去i位置俄高度
// 主要想法就是得到每个位置能保存的水的大小
if(height.length <= 2){
return 0;
}
int n = height.length;
int[] leftMax = new int[n];
int[] rightMax = new int[n];
leftMax[0] = height[0];
for(int i=1;i<n;i++){
leftMax[i] = Math.max(leftMax[i-1], height[i]);
}
rightMax[n-1] = height[n-1];
for(int j=n-2;j>=0;j--){
rightMax[j] = Math.max(rightMax[j+1], height[j]);
}
int ans = 0;
for(int k =0;k<n;k++){
ans += Math.min(leftMax[k],rightMax[k])-height[k];
}
return ans;
}
}
单调栈:困难柱状图中最大的矩形
class Solution {
int largestRectangleArea(int[] heights) {
// 动态规划的难点在于找到以一个小于他的元素的下标
int size = heights.length;
int[] minLeftIndex = new int[size];
int[] minRightIndex = new int[size];
minLeftIndex[0] = -1; // 避免里面的while陷入死循环,避免第一个数字加入计算
for(int i=1;i<size;i++){
int t = i - 1; //从左侧开始依次向左寻找
while(t>=0 && heights[t] >= heights[i]){ // 注意这里是while循环寻找最小值 不能像之前的if了
t = minLeftIndex[t]; // 动态规划就体现在这句话,知道找到不满足的
}
minLeftIndex[i] = t; // 找到第一个小于当前i的最小下标
}
minRightIndex[size -1] = size; // 避免最后一个数字加入计算
for(int i=size-2; i>=0;i--){
int t = i + 1;
while(t<size&& heights[t] >= heights[i]){
t = minRightIndex[t];
}
minRightIndex[i] = t;
}
int result = 0;
for(int i=0;i<heights.length;i++){
int sum = heights[i] * (minRightIndex[i]-minLeftIndex[i] -1);
result = Math.max(sum, result);
}
return result;
}
}