文章是个人视频学习笔记,视频是b站尚硅谷的韩顺平老师的算法结构
视频地址
https://www.bilibili.com/video/av54029771?p=42
后缀计算器的关键是,将我们易于理解的中缀表达式,转换成计算机容易理解的后缀表达式,再进行运算
中缀转后缀思路如下:
逆波兰计算器的计算思路如下:
代码如下
在这里插入package 算法.栈.逆波兰计算器后缀表达式;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolanNotation {
public static void main(String[] args) {
//完成将中缀表达是转成后缀表达是的功能
//1+((2+3)*4)-5 =>转换成 1 2 3 + 4 * + 5 -
//1.把中缀表达式转换成一个list,为了方便操作,直接对字符串操作不方便
//2把得到的中缀表达式list 转换成后缀报表达式
String expression="1+((2+3)*4)-5";
List<String> InfixExpressionlist = toInfixExpression(expression);
System.out.println(InfixExpressionlist);
//中缀list转换成后缀list
List<String> parseSuffixExpression = parseSuffixExpressionList(InfixExpressionlist);
//输出转换成功的后缀list
System.out.println(parseSuffixExpression);
//计算值
System.out.println(calculate(parseSuffixExpression));
}
//将中缀表达式转换成List
public static List<String> toInfixExpression(String s){
List<String> ls = new ArrayList<>();
int i = 0; //用于遍历中缀表达式字符串
String str ;//用于对多位数的拼接
char c ; //每遍历一个字符,就放入到c
do{
//如果c是一个非数字,直接加入到ls
if((c=s.charAt(i))<48||(c=s.charAt(i))>57){
ls.add(""+c);
i++;
}else {//如果是数字,需要考虑多位数
str="";//str置空
//判断i<s.length防止越界,和下一位是否是数字
while (i<s.length()&&(c=s.charAt(i))>=45&&(c=s.charAt(i))<=57){
str+=c;
i++;
}
ls.add(str);
}
}while (i<s.length());
return ls;
}
//把中缀表达式对应的list转换成后缀表达式对应的list
public static List<String> parseSuffixExpressionList(List<String> ls){
//定义两个栈
Stack<String> s1 =new Stack<>();//符号栈
//因为s2在计算结果中没有出栈操作,而且得到结果后要逆序输出,所以我们用list代替它
//Stack<String> s2 =new Stack<>();//存储中间结果的栈
List<String> s2 = new ArrayList<>();
//遍历 ls
for (String item:ls) {
//如果是一个数,加入s2
if(item.matches("\\d+")){
s2.add(item);
}else if(item.equals("(")){
//如果是左括号,直接加入s1
s1.push(item);
}else if(item.equals(")")){
//如果是右括号,依次弹出s1中的符号,压入s2,直到遇到左括号
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();//弹出左括号
}else {
//考虑运算符优先级
//当item小于s1栈顶运算符,将s1栈顶弹出,并且加入到s2中,并且再次与次订元素比较优先级
//直到item优先级小于栈顶元素
while (s1.size()!=0&&Operation.getValue(s1.peek())>=Operation.getValue(item)){
s2.add(s1.pop());
}
//最后还要将扫描到的item入栈
s1.push(item);
}
}
//扫描完毕,将s1中的剩余运算符加入到s2
while (s1.size()!=0){
s2.add(s1.pop());
}
return s2; //因为用list存放结果,此时s2顺序输出就是对应的后缀表达式对应的
}
//完成对逆波兰表达式的遍历计算
public static int calculate(List<String> list){
//创建栈处理后缀表达式
Stack<String> stack =new Stack<>();
//遍历ls
for (String item:list) {
//使用正则表达式取出数
if(item.matches("\\d+")){//匹配多位数
stack.push(item);
} else {//如果是符号,弹出两位数,运算后再入栈
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res =0;
switch (item){
case "+":
res=num1+num2;
break;
case "-":
res=num2-num1;
break;
case "*":
res=num2*num1;
break;
case "/":
res=num2/num1;
break;
default:
throw new RuntimeException("非法运算字符");
}
//把结果转换成字符串后入栈
stack.push(String.valueOf(res));
}
}
//最后留在stack的String类型数字就是结果
return Integer.parseInt(stack.pop());
}
}
//编写返回运算符优先级的类
class Operation{
private static int ADD=1;//加
private static int SUB=1;//减
private static int MUL=1;//乘
private static int DIV=1;//除
public static int getValue(String operation){
int result =0;
switch (operation){
case "+":
result=ADD;
break;
case "-":
result=SUB;
break;
case "*":
result=2;
break;
case "/":
result=2;
break;
case "(":
result=0;
break;
default:
throw new RuntimeException("非法运算符");
}
return result;
}
}
代码片