1. 逆波兰表达式
题目:
给出一个算数式的后缀表达式,我们来求他最后算数值.
在解题之前我们来认识一下中缀表达式,和后缀表达式(逆波兰表达式
我们在写数学遇到的那种形式的算数表达式就是中缀表达式,我们要从中缀表达式变为后缀表达式(逆波兰式),计算机时不知道式子的计算顺序的,因此我们给算数顺序加上括号来提醒计算机来进行计算,然后我们把当前括号内的式子的运算符移到右括号外面比如 ((a + b) * c) ->((a b)+c)*即可
题目解析:
我们取出字符串的每一个字符,判断是否为算数运算符,不是就说明是数字,我们就进行入栈操作,如果是,我们就取出栈中的俩个元素,第一个元素作为栈的右操作数,第二个元素作为栈的左操作数,然后我们进行计算,算出的结果再入栈,然后继续取出字符串的字符,循环直到遍历完字符串为止,我们就算出了最终值,返回结果即可.
整体代码:
注意,我们从字符串取出来的字符如果要进行算数运算,我们就要进行类型的转换,比如我们这一题,把数字字符转换为整数 Integer.parseInt(x);然后我们的不同运算符的运算我们是通过switch-case来进行表述的.
public class Test1 {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
//遍历tokens数组
for (String x : tokens) {
//如果是数字就直接放到栈里面
if(!isOperation(x)) {
//把数字字符转换为整数
stack.push(Integer.parseInt(x));
//如果不是数字
}else {
int num2 = stack.pop();//右操作数
int num1 = stack.pop();//左操作数
switch (x) {
case "+":
stack.push(num1+num2);
break;
case "-":
stack.push(num1-num2);
break;
case "*":
stack.push(num1*num2);
break;
case "/":
stack.push(num1/num2);
break;
}
}
}
return stack.pop();
}
//判断x是不是操作符
private boolean isOperation(String x) {
if (x.equals("+") || x.equals("-") || x.equals("*") || x.equals("/")) {
return true;
}
return false;
}
}
2. 有效的括号
题目:
我们给出一个字符串,里面放着一堆括号,我们设置里面的括号有三种(),{}.[],我们要判断里面的括号是不是成对的,并且能相互匹配上的.
题目解析:
我们只需要解决三种非正常情况:括号不匹配,左括号多,右括号多.
解题步骤: 1.左括号入栈. 2.遇到右括号和栈顶元素的左括号是否匹配
整体代码:
我们遍历字符串,把每一个字符取出来,然后我们要对字符进行(字符串里面都是那三种括号)判断是不是左括号,如果是左括号就入栈,如果不是左括号,就说明是右括号,我们就进行匹配,取出我们栈里面的元素和当前的字符进行匹配匹配成功就出栈,失败就返回false,此时,我们考虑上面提到的其他俩种情况,当我们的栈空,但是字符串还没遍历完,说明了我们的右括号数目大于左括号数返回false.如果我们的.当我们遍历完字符串之后,我们发现了栈里面还有元素,说明我们的左括号数目大于右括号数返回false.
public class Test2 {
public boolean isValid(String s) {
//设定一个栈
Stack<Character> stack = new Stack<>();
//1.遍历字符串
for (int i = 0; i < s.length(); i++) {
//把字符串每一个字符取出来
char ch = s.charAt(i);
//2. 判断是不是左括号
if(ch == '(' || ch == '{' || ch == '[') {
//是左括号就入栈
stack.push(ch);
}else {
//不是左括号,那么就是右括号就进行匹配
if(stack.empty()) {
//TODO 如果栈是空的,说明栈空字符串不空,右括号多
return false;
}else {
//如果不是空的,就进行匹配
char ch1 = stack.peek();//栈里面放的是左括号
if((ch1 == '(' && ch == ')') || (ch1 == '[' && ch == ']') || (ch1 == '{' && ch == '}')) {
stack.pop();//出栈进行匹配
}else {
//TODO 如果左右括号不匹配
return false;
}
}
}
}
if(!stack.empty()) {
//TODO 如果遍历完之后,栈还有元素,说明字符串空了,左括号多
return false;
}
return true;
}
}
3. 栈的压入,弹出序列
栈的一些特性补充,有些题是这种排出栈顺序的题,我们就要注意一下,如图显示,如果说明能够在入栈的期间出栈,我们就有不同的出栈顺序.
题目:
我们给一个入栈数组和出栈数组,判断这个入栈顺序和出栈顺序是否匹配.
题目解析:
解题步骤:
1. 遍历push数组,把元素放到栈中
2. 每push一个元素,就和pop数组的元素比较
3. 如果相等j++ 且出栈
4. 如果不相等想办法入栈
整体代码:
我们先遍历push数组,让里面的元素入栈,然后我们判断pop数组里面的元素是否和栈顶元素一样,一样我们就把栈顶元素出栈,此时我们需要判断栈是不是空的,我们的j下标是不是在合法范围内.最后我们遍历完了数组,栈为空说明我们的压入顺序能匹配上弹出顺序.
//TODO 栈的压入,弹出序列
public class Test3 {
public boolean IsPopOrder(int[] pushA,int[] popA) {
//先创建一个栈
Stack<Integer> stack = new Stack<>();
int j = 0;
//遍历pushA数组
for (int i = 0; i < pushA.length; i++) {
//每次遍历先push进栈
stack.push(popA[i]);
//我们再判断popA的元素和pushA的元素是否一样
//栈是否为空,j是否在合法下标内,栈的元素是否等于弹出数组的元素
while (!stack.empty()&& j < popA.length && stack.peek() == popA[j]) {
//如果相等我们就出栈
stack.pop();
j++;
}
}
return stack.empty();
}
}
4. 最小栈
题目:
题目解析:
push方法
pop方法
其他代码看看整体代码即可
然后我们这里注意一个地方
如果只写minStack.peek() > val,那么当栈里面有多个相同的最小值的时候就会出问题,如图
整体代码:
//TODO 最小栈
public class Test4 {
class MinStack {
//定义俩个栈
private Stack<Integer> minStack;
private Stack<Integer> stack;
public MinStack() {
minStack = new Stack<>();
stack = new Stack<>();
}
public void push(int val) {
//普通栈一定要放元素
stack.push(val);
if(minStack.empty()) {
//最小栈如果为空也要放元素
minStack.push(val);
} else {
//如果最小栈的元素比val大,那么也要放进去
if(minStack.peek() >= val) {//这个等号要注意,因为如果多个最小值的话也要放进去,不然最小栈就为空了
minStack.push(val);
}
}
}
public void pop() {
//普通栈什么情况下都要弹出
int val = stack.pop();
if (!minStack.empty()) {//在最小栈不为空的情况下
if (val == minStack.peek()) {
//最小栈只有在和普通栈的val一样才会pop
minStack.pop();
}
}
}
//获取当前普通栈的值
public int top() {
return stack.peek();
}
//最小栈的peek,每次通过这个方法获取最小值
public int getMin() {
if(!minStack.empty()) {
return minStack.peek();
}
return -1;
}
//当写代码出错了,记得调试
//1. 看哪个测试用例错了
//2. 拿着这个测试用例区画图
//3. 不能光看代码
}
}