前言
楼主最近在进行学习数据结构与算法,遂将所学知识记录起来,一来是为了自己以后复习,二来是提供给大家交流,发现其中的问题,共同来优化算法。
一.算法思想
- 从键盘中输入需要计算的 中缀表达式
- 通过方法toList将中缀表达式中的每个组员存放到字符数组list中
- 遍历字符数组list,通过方法infixtosuffix将中缀表达式转换为后缀表达式finallist
- 通过后缀表达式进行计算的到最后的结果
二.代码实现
package ReversePolishCalculator;
import java.beans.Expression;
import java.util.*;
public class ReversePolishCalculator {
public static void main(String[] args) {
//输入一个中缀表达式
Scanner reader = new Scanner(System.in);
String expression = reader.nextLine();
//通过方法toList将中缀表达式中的每个组员存放到字符数组list中
List<String> list=toList(expression);
//遍历字符数组list,通过方法infixtosuffix将中缀表达式转换为后缀表达式finallist
List<String> finalList=infixtosuffix(list);
//检查是否正确地将中缀表达式转换为后缀表达式
System.out.println(finalList);
//通过后缀表达式进行计算
int res=calculate(finalList);
//输出最终的结果
System.out.println(res);
}
//将中缀表达式转换到List中
public static List<String> toList(String expression) {
//定义一个List存放中缀表达式中的内容
List<String> list = new ArrayList<String>();
String str=""; //对多位数进行拼接
char c; //每遍历到一个字符就放到c中
//方法一:
int i=0; //expression的索引
do {
//如果c是一个非数字的符号,则加入到list中
if ((c = expression.charAt(i)) < 48 || (c = expression.charAt(i)) > 57) {
list.add(c + "");
i++;
} else { //如果是一个数,需要考虑多位数问题
str = "";
while (i<expression.length() && (c = expression.charAt(i)) >= 48 && (c = expression.charAt(i)) <= 57){
str+=c;
i++;
}
list.add(str);
}
}while (i<expression.length());
return list;
}
//将中缀表达式转为后缀表达式(重点!!!)
public static List<String> infixtosuffix(List<String> list){
Stack<String> s1=new Stack<String>(); //符号栈
Stack<String> s2=new Stack<String>(); //数字栈
//遍历list
for (String item:list){
//如果是一个数,入栈s2
if(item.matches("\\d+")){
s2.push(item);
}else if(item.equals("(")){
s1.push(item);
}else if(item.equals(")")){ //如果是),则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,并将这一对括号丢弃
while (!s1.peek().equals("(")){
s2.push(s1.pop());
}
s1.pop(); //丢弃左括号
}else {
//当item的优先级<=s1栈顶的运算符的优先级,将s1栈顶的运算符弹出并压入到s2中,item继续与s1中的栈顶元素比较,直至item的优先级>栈顶元素的优先级或栈顶元素为)
while (!s1.empty() && Operation.getValue(s1.peek())>=Operation.getValue(item)){
s2.push(s1.pop());
}
//比较完成后将item入栈s1
s1.push(item);
}
}
//将s1中剩余的运算符压入s2中
while (!s1.empty()){
s2.push(s1.pop());
}
//将s2的元素依次弹出,存入数组list中,并原地逆置,所得结果及为最终的后缀表达式(这里s2可以声明为数组,因为s2始终没有进行pop操作,如果是数组的话直接输出就可以)
List<String> finalList=new ArrayList<String>();
while (!s2.empty()){
finalList.add(s2.pop());
}
int len=finalList.size();
int index=0;
while (index<len/2){
String temp=finalList.get(index);
finalList.set(index,finalList.get(len-index-1));
finalList.set(len-index-1,temp);
index++;
}
return finalList;
}
//利用后缀表达式进行计算
public static int calculate(List<String> finallist) {
Stack<String> stack = new Stack<String>();
for (String item : finallist) {
//用正则表达式取出数
if (item.matches("\\d+")) { //匹配的是数字
stack.push(item);
} else { //匹配的是符号
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")) {
res = num2 + num1;
} else if (item.equals("-")) {
res = num2 - num1;
} else if (item.equals("*")) {
res = num2 * num1;
} else if (item.equals("/")) {
res = num2 / num1;
} else {
throw new RuntimeException("符号错误");
}
stack.push(res + "");
}
}
return Integer.parseInt(stack.pop());
}
}
创建一个类,用来处理运算符的优先级
package ReversePolishCalculator;
public class Operation {
private static final int ADD=1;
private static final int SUB=1;
private static final int MUL=2;
private static final int DIV=2;
public static int getValue(String operation){
int res=0;
switch (operation){
case "+":
res=ADD;
break;
case "-":
res=SUB;
break;
case "*":
res=MUL;
break;
case "/":
res=DIV;
break;
default:
System.out.println("不存在该运算符");
break;
}
return res;
}
}
三.测试案例
- 中缀表达式:(30+4)5-6
逆波兰表达式:30 4 + 5 * 6 -
执行结果:
2.中缀表达式100(100-50)+2-47/7
逆波兰表达式:
执行结果: