力扣-栈
栈:先进后出
内置栈
// "static void main" must be defined in a public class.
public class Main {
public static void main(String[] args) {
// 1. Initialize a stack.
Stack<Integer> s = new Stack<>();
// 2. Push new element.
s.push(5);
s.push(13);
s.push(8);
s.push(6);
// 3. Check if stack is empty.
if (s.empty() == true) {
System.out.println("Stack is empty!");
return;
}
// 4. Pop an element.
s.pop();
// 5. Get the top element.
System.out.println("The top element is: " + s.peek());
// 6. Get the size of the stack.
System.out.println("The size is: " + s.size());
}
}
最小栈
有效的括号
题解1
使用栈,如果想要进栈的是某种右括号,则检查栈顶是否是对应的左括号,如果是就将其出栈,如果不是就返回false。
这里为了防止第一个进栈的是右括号,pop时会报错,所以先在栈中放了一个元素。
class Solution {
public boolean isValid(String s) {
Stack<Character> process=new Stack<Character>();
process.push('0');
for(char i:s.toCharArray()) {
switch (i) {
case ')':
if(process.pop()!='(')
return false;
break;
case']':
if(process.pop()!='[')
return false;
break;
case'}':
if(process.pop()!='{')
return false;
break;
default:
process.push(i);
}
}
return process.size()==1;
}
}
题解2
使用了hashmap
class Solution {
public boolean isValid(String s) {
HashMap<Character, Character> pairsHashMap=new HashMap<Character, Character>();
pairsHashMap.put(')', '(');
pairsHashMap.put('}', '{');
pairsHashMap.put(']', '[');
Stack<Character> process=new Stack<Character>();
for(char i:s.toCharArray()) {
if(pairsHashMap.containsKey(i)) {
if(process.isEmpty()||process.pop()!=pairsHashMap.get(i))
return false;
}
else
process.push(i);
}
return process.size()==0;
}
}
每日温度
思路是使用单调栈以及存储的是序号。
单调栈:保证栈中自底向上序号对应的温度是单调不增的
存储序号:方便对数组取值以及存值
过程:
当栈为空时直接压入
当栈非空时,比较即将压入栈的序号对应的温度与栈顶的序号对应的温度,
如果即将压入栈的序号对应的温度大于栈顶序号对应的温度,
则将栈顶序号排出(找到温度更高的日期),
并将两个序号之差(也就是要等待的天数)填到栈顶序号对应的位置。
题解
class Solution {
public int[] dailyTemperatures(int[] T) {
Stack<Integer> Temperature=new Stack<Integer>();
for(int i=0;i<T.length;i++) {
if(Temperature.isEmpty()) {
Temperature.push(i);
continue;
}
while (!Temperature.isEmpty()&&T[i]>T[Temperature.peek()]) {
//当栈非空且现在的数大于栈顶的数时,将栈顶的数排出
int ii=Temperature.pop();
T[ii]=i-ii;
//将栈顶数与现在的数相差的天数填到相应位置
}
Temperature.push(i);
//将现在的数压入栈中
}
int length=Temperature.size();
for(int i=0;i<length;i++)
//这里本来写的是i<Temperature.size(),但是因为pop后size会变,所以会出错
T[Temperature.pop()]=0;
//处理未找到更高天气的日期
return T;
}
}
直方图的水量
正好每日一题出了最小栈,就一并记录在这里
思路看代码注释↓
题解
class Solution {
public int trap(int[] height) {
Stack<Integer> stack= new Stack<Integer>();
//单调递减栈
//这里定义一个概念,叫柱的最高蓄水量,就是指当前柱上头最多能储蓄多少水
int res=0;
int max=0;
int tem=0;
for(int i=0;i<height.length;i++) {
if (stack.isEmpty()&&height[i]==0) continue;
if(stack.isEmpty()||height[i]<height[stack.peek()]) stack.push(i);
//如果栈为空或现有索引对应的元素小于栈顶索引对应的元素,即当前柱低于前面的柱压入栈
else {
while(!stack.isEmpty()&&height[i]>=height[stack.peek()]) {
//当栈不为空且现有索引对应的元素大于栈顶索引对应的元素时,将栈中索引排出,即排出比当前柱低的柱
//目的就是保持单调递减栈
tem=stack.pop();
//存储最后一个被排出栈的索引,因为计算水量时需要分情况
}
if(stack.isEmpty()) {
//如果现有索引对应的元素大于之前所有索引对应的元素,即当前柱高于之前的所有柱,
//则次高柱到当前柱之间的所有柱的最高蓄水量为次高柱的高度
//且后面再出现更高柱时也不会影响这些柱的最高蓄水量
for(int j=tem;j<i;j++) {
res+=(height[tem]-height[j]);
}
}
else {
for(int j=stack.peek()+1;j<i;j++) {
//如果前面还有更高的柱时,则更高柱与此柱之间的所有柱最高蓄水量为此柱的高度
//同时后面可能会出现比此柱更高的柱,也就是会修改更高柱与次柱之间所有柱的最高蓄水量
//所以这里将次柱到前面的最高柱之间的所有柱高度都设为此柱的高度(也就是将存储水分步走)
res+=(height[i]-height[j]);
height[j]=height[i];
}
}
stack.push(i);
}
}
return res;
}
}
优化
在一解的基础上,进行优化。
class Solution {
public int trap(int[] height) {
Stack<Integer> stack= new Stack<Integer>();
//单调递减栈
int res=0;
int tem=0;
for(int i=0;i<height.length;i++) {
if (stack.isEmpty()&&height[i]==0) continue;
while(!stack.isEmpty()&&height[i]>height[stack.peek()]) {
//当栈不为空且现有索引对应的元素大于栈顶索引对应的元素时,将栈中索引排出
//目的就是保持单调递减栈
tem=stack.pop();
//存储最后一个被排出栈的索引
if (stack.isEmpty())break;
int left=stack.peek();
res+=(Math.min(height[left],height[i])-height[tem])*(i-left-1);
}
stack.push(i);
//将当前索引压入栈
}
return res;
}
}
逆波兰表达式
思路
还是比较简单的题目的,就是用一下栈,如果是符号的话就进行运算,注意除法和减法的时候要改变顺序。
题解
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack=new Stack<Integer>();
for(String i:tokens) {
//如果是符号位的话进行操作,如果是数值,直接进栈
if(i.equals("*")) stack.push(stack.pop()*stack.pop());
//注意这里的四个if下面三个要用else if,不然就会出问题
else if(i.equals("/")) {
//减法和除法要注意前后顺序,栈的pop顺序与表达式顺序是相反的
int a=stack.pop();
int b=stack.pop();
stack.push(b/a);
}
else if(i.equals("+")) stack.push(stack.pop()+stack.pop());
else if(i.equals("-")) {
int a=stack.pop();
int b=stack.pop();
stack.push(b-a);
}
else stack.push(Integer.valueOf(i));
}
return stack.pop();
}
}
改进
用数组来实现栈,一开始用的是Arraylist,提升的不明显,改成了int[],注意这里就需要考虑一下数组的最长长度,最坏情况下,所有数字都在前面,符号在后面,这时存储数字需要的长度就是(N+1)/2。
class Solution {
public int evalRPN(String[] tokens) {
int[] stack=new int[(tokens.length+1)/2];
int length=0;
for(String i:tokens) {
if(i.equals("*")) {
stack[length-2]=stack[length-2]*stack[length-1];
length--;
}
else if(i.equals("/")) {
stack[length-2]=stack[length-2]/stack[length-1];
length--;
}
else if(i.equals("+")) {
stack[length-2]=stack[length-2]+stack[length-1];
length--;
}
else if(i.equals("-")) {
stack[length-2]=stack[length-2]-stack[length-1];
length--;
}
else stack[length++]=Integer.valueOf(i);
}
return stack[length-1];
}
}
再改进
用了switch-case,像这种分支较多的情况下,用switch这种随机跳的效率会比一次判断的if-else效率更高
class Solution {
public int evalRPN(String[] tokens) {
int[] stack=new int[(tokens.length+1)/2];
int length=0;
for(String i:tokens) {
switch (i) {
case("*"):
stack[length-2]*=stack[length-1];
length--;
break;
case("/"):
stack[length-2]/=stack[length-1];
length--;
break;
case ("+"):
stack[length-2]+=stack[length-1];
length--;
break;
case ("-"):
stack[length-2]-=stack[length-1];
length--;
break;
default:stack[length++]=Integer.valueOf(i);
}
}
return stack[length-1];
}
}