问题引入
我们平常计算的表达式通常为中缀表达式,例如;1+((2+3)*4)+5,而这样的方式对计算机并不友好,对于计算机而言,更好的为前缀表达式和后缀表达式,在这两者之间,对于我们而言,后缀表达式更好理解一些,例如:3 4 + 5 * 6 - ,计算过程如下:
3 4 + 先运算得到7,然后表达式就变为7 5 * 6 -,继续计算下去
,35 6 -,最后得到29。
前缀表达式又被称为波兰表达式,而后缀表达式被称为逆波兰表达式。
接下来就介绍如何用程序来计算逆波兰表达式。
思路分析
就拿这个例子来说1 2 3 + 4 * + 5 -。
前置准备
以其为字符串来计算,首先把放入集合中,每一个字符都当作一个字符串,而此时我们还需考虑多位数的计算,如果某表达式为26 + 2,我们不能把它变为2,6,+,2,于是我们在遍历字符串时需要一个中间变量,来记录当前的数,如果当前读到的是符号,就把中间变量放入集合中,同时把中间变量置空,然后把符号放入集合中,依次遍历最后得到盛放逆波兰字符串的集合。
具体计算
当我们成功拿到逆波兰表达式的集合后,开始遍历集合,那么什么类型的数据结构适合运算这个表达式呢?
注意,当我们运算+ *法时,弹出栈的数据计算顺序可以随便写,但是当遇见- / 法时,我们需要用栈下面的值-或/上面的值。运算过程的思路就是这样。
具体代码实现
public class PolandNotation {
public static void main(String[] args) {
String centerExpression = "1 2 3 + 4 * + 5 -";
List<String> result = toSuffixExpressionList(centerExpression);
System.out.println("后缀表达式的结果为: " + calculator(result));
}
//后缀字符串转集合
public static List<String> toSuffixExpressionList(String s){
//定义一个list,存放后缀表达式对应的内容
List<String> ls = new ArrayList<>();
int i = 0; //起始索引
String str = ""; //对多位数的拼接
char c; //每遍历到一个字符就放入c中
do {
if((c = s.charAt(i)) < '0' || (c = s.charAt(i)) > '9') {
if(c == ' '){ //当字符为空格时跳过
i++;
continue;
}
ls.add("" + c);
i++;
} else {
str = "";
while (i < s.length() && ((c = s.charAt(i)) >= '0' && (c = s.charAt(i)) <= '9' || (c = s.charAt(i)) == '.')) {
str += c;
i++;
}
ls.add(str);
}
} while (i < s.length()) ;
return ls;
}
//完成对逆波兰表达式的运算
public static double calculator(List<String> rpnList) {
Stack<Double> stack = new Stack<>();
for (String temp : rpnList) {
if (!isOper(temp)) {
stack.push(Double.valueOf(temp));
} else {
double num1 = Double.valueOf(stack.pop());
double num2 = Double.valueOf(stack.pop());
stack.push(cal(num1, num2, temp));
}
}
return stack.pop();
}
//判断当前字符是否为符号
public static boolean isOper(String ch) {
return ch.equals("+") || ch.equals("-") || ch.equals("*") || ch.equals("/") || ch.equals("(") || ch.equals(")");
}
//计算方法
public static double cal(double num1, double num2, String oper) {
//用于存放计算的结果
double res = 0.0;
switch (oper) {
case "+":
res = num1 + num2;
break;
case "-":
res = num2 - num1; //注意顺序
break;
case "*":
res = num1 * num2;
break;
case "/":
if(num1 == 0){
throw new RuntimeException("被除数不能为0");
}
res = num2 / num1;
break;
default:
break;
}
return res;
}
}