1、后缀表达式
中缀表达式如(3+4)*5-6是我们人类在书写数学表达式时最长使用的一种书写方式,但对于计算机来说,后缀表达式(又称为逆波兰表达式)更利于计算。
- 中缀表达式: (3+4)*5-6 -> 后缀表达式(逆波兰表达式):3 4 + 5 * 6 -
- 中缀表达式:4*5-8+60+8/2 -> 后缀表达式(逆波兰表达式):4 5 * 8 - 60 + 8 2 / +
后缀计算器的设计步骤如下:
- 将中缀表达式String转为中缀表达式List;
- 将中缀表达式List转为后缀表达式List;
- 将后缀表达式存在栈Stack中,运用栈的特性完成计算。
2、Java代码
package DataStructures.Stack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
/**
* @author yhx
* @date 2020/09/21
*/
public class PolandNotation {
/**
* ASCII码表中数字字符0和9对应的编号
*/
private static final int ASCII_0 = 48;
private static final int ASCII_9 = 57;
private static final String BRACKET_LEFT = "(";
private static final String BRACKET_RIGHT = ")";
public static void main(String[] args) {
//定义一个中缀表达式:
String expression = "(4+5)*(8+60)+8/2";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println("中缀表达式:" + infixExpressionList);
List<String> suffixExpression = parseSuffixExpressionList(infixExpressionList);
System.out.println("后缀表达式:" + suffixExpression);
System.out.printf("表达式计算结果 = %d",calculate(suffixExpression));
}
/**
* 将中缀表达式 list 转为后缀表达式 list
*
* @param ls 中缀表达式list
* @return 返回后缀表达式 list
*/
public static List<String> parseSuffixExpressionList(List<String> ls) {
//定义一个栈s1用于存储操作符 和 一个s2用于存储中间结果
Stack<String> s1 = new Stack<>();
List<String> s2 = new ArrayList<>();
//遍历ls
for (String item : ls) {
//如果是数,就入list
if (item.matches("\\d+")) {
s2.add(item);
} else if (item.equals(BRACKET_LEFT)) {
//如果是左括号就将括号入栈s1
s1.push(item);
} else if (item.equals(BRACKET_RIGHT)) {
//如果是右括号")",则依次弹出s1栈顶的运算符,并转存到s2中,弹到遇到左括号"("为止,此时就将一对括号丢弃
//peek用于查看栈顶的元素值
while (!s1.peek().equals(BRACKET_LEFT)) {
s2.add(s1.pop());
}
//将栈顶的左括号删除
s1.pop();
} else {
//当item的优先级小于等于s1栈顶运算符的优先级,则将s1栈顶的运算符弹出并加入到s2中,重复该操作
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
s2.add(s1.pop());
}
//当s1中优先级比item高的运算符都被转移到s2之后,将item压入s1
s1.push(item);
}
}
//将s1中剩余的运算符以及弹出并加入到s2中
while (s1.size() != 0) {
s2.add(s1.pop());
}
//最终存放在s2中数据,就是逆波兰表达式对应的list
return s2;
}
/**
* 将中缀表达式转为对应的List
*
* @param s 中缀表达式String
* @return 返回该中缀表达式List类型
*/
public static List<String> toInfixExpressionList(String s) {
//存放中缀表达式List
List<String> ls = new ArrayList<>();
//指针,遍历中缀表达式字符串
int i = 0;
//对多位数拼接
StringBuilder str;
do {
//如果c是一个非数字,就将其加入到ls中
if (s.charAt(i) < ASCII_0 || s.charAt(i) > ASCII_9) {
ls.add(s.charAt(i) + "");
i++;
}
//如果是一个数('0'[48] ~ '0'[57]),则需要考虑多位
else {
//先将str置为空"",
str = new StringBuilder();
while (i < s.length() && s.charAt(i) >= ASCII_0 && s.charAt(i) <= ASCII_9) {
//拼接,str.append(c)等价于str += c
str.append(s.charAt(i));
i++;
}
ls.add(str.toString());
}
} while (i < s.length());
return ls;
}
/**
* 将str字符串转为List表的形式,便于遍历操作
*
* @param suffixExpression 后缀表达式的str字符串
* @return 返回后缀表达式List
*/
public static List<String> getListString(String suffixExpression) {
//以空格为标志,将字符串suffixExpression分割为字符串数组
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<>();
//将字符串数组中的元素全部添加到字符串列表中
Collections.addAll(list, split);
return list;
}
/**
* 将后缀表达式List存到栈中,并完成计算
*
* @param ls 后缀表达式List
* @return 返回计算结果
*/
public static int calculate(List<String> ls) {
//创建一个栈即可
Stack<String> stack = new Stack<>();
//遍历ls
for (String item : ls) {
//如果item是数,则入栈。matches是正则表达式的匹配函数,"\\d+"表示匹配数字(+说明可匹配多位数)
if (item.matches("\\d+")) {
stack.push(item);
} else {
//如果匹配到操作符,就从栈中取出两个数计算,再将结果放回栈中
//因为栈中的元素类型是String,所以需要用Integer.parseInt转为整数型
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res;
switch (item) {
case "+":
res = num1 + num2;
break;
case "-":
res = num1 - num2;
break;
case "*":
res = num1 * num2;
break;
case "/":
res = num1 / num2;
break;
default:
throw new RuntimeException("运算符有误");
}
//将运算结果重新放回栈中。但res是整型,栈是String类型,此处用个小技巧可将res强转为String:res + ""
stack.push(res + "");
}
}
//最后留在栈中的就是运算结果
return Integer.parseInt(stack.pop());
}
}
/**
* 用于比较运算符优先级的类
*/
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;
/**
* @param operation 操作符
* @return 返回对应的优先级
*/
public static int getValue(String operation) {
int result = 0;
switch (operation) {
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
break;
}
return result;
}
}
3、运行结果
中缀表达式:[(, 4, +, 5, ), *, (, 8, +, 60, ), +, 8, /, 2]
后缀表达式:[4, 5, +, 8, 60, +, *, 8, 2, /, +]
表达式计算结果 = 616