波兰表达式
我们常见人类容易理解的计算公式如1+2 (3+2)等属于中缀表达式,这种表达式的特点是如何人们阅读的习惯,但是计算机在处理的时候需要对公式进行运算顺序的处理,不利于计算机的理解,从而计算机学家发明了波兰表达式这种易于计算机理解的表达式。
- 前缀表达式,又称波兰表达式,特点在于运算符在前操作数在后如 - + * 1 2 3 4,计算顺序从右至左,
- 依次将3 2 1 压入stack
- 遇操作符 pop出stack中的1 2 计算 得到 2 重新push到stack中
- 遇+操作符 弹出 stack中 2 3 计算 得到 5 重新push到stack中
- 遇-操作符 弹出 5 和 4 又栈顶为被减数 即 5-4 重新push到stack中
- 最后stack只有一个数为计算结果
- 中缀表达式 即为我们普通常用的计算公式
- 后缀表达式,又称逆波兰表达式,因为前缀表达式并不如何人类的阅读习惯,所有出现了后缀表达式,从左往右依次计算,最终堆栈的最后一个数即为结果
- 例如 1 + ( ( 2 + 3 )* 4) - 5 转为逆波;兰表达式为 1 2 3 + 4 * + 5 -
- 后缀表达式的计算规则为如遇到数字直接push到堆栈如遇到操作符从堆栈中弹出2个数计算完之后重新压入堆栈直到堆栈只有一个数
- 逆波兰表达式计算规则 1 2 3 数字 压入 堆栈
- 遇到+号操作符 弹出2个数字 3 2 计算得到 5 压入堆栈 其次堆栈从栈底到栈顶依次为 1 5
- 遇到数字4压入 1 5 4
- 遇到*号操作符 弹出 4 5 计算 20 压入 此时为 1 20
- 遇到+号 弹出 20 1 计算 21 压入 21
- 遇到数字5 压入 21 5
- 遇到-号操作符 弹出 5 21后面一个数减去前面一个数即 21 - 5 得到16
- 中缀表达式转后缀表达式
- 首先初始化两个栈stack,运算符栈stack1 和中间结果栈stack s2
- 从左往右开始扫描中缀表达式
- 遇到数字直接压入 s2
- 遇到操作符需要做特殊处理
- 如果是左括号( 直接压入s1
- 如果是右括号 ) 需要从左边s1中依次弹出若干操作符压入s2 直到遇到 左括号)为止,并且最后叫匹配的左括号弹出 即消除了一对括号
- 如果是正常操作符
- 如果当前操作符的优先级大于 s1中栈顶的优先级 则直接压入s1
- 如果s1为空 或者 s1的栈顶为 左括号( 直接压入s1
- 如果当前操作符优先级小于等于 s1栈顶操作符的优先级,则从s1的栈顶弹出操作符并压入到s2中,一直循环操作第三步,即一直进行到s1为空或者s1栈顶的操作符优先级小于了当前操作符,最终将当前操作符压入到s1中
- 最终将s1中剩余的所有操作符依次弹出压入s2
- 最终将s2的栈数据反转即(从栈底到栈顶)得到中缀表达式
代码实现
public class PolandCalculator {
// 构造器传入的中缀表达式
public final String expression;
// 用于保存解析出来的后缀表达式
public List<String> polishExpressionList;
/**
* 中缀表达式
*
* @param expression
*/
public PolandCalculator(String expression) {
this.expression = expression;
init();
}
/**
* 中缀表达式转后缀表达式的方法
*/
public void init() {
// 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
Stack<String> s1 = new Stack<>();
// 因为s2中没有任何pop的操作 即用list处理更加方法,而且也省去了stack反转的麻烦,因为list本身就是顺序添加的,不需要像stack一样先进后出
ArrayList<String> s2 = new ArrayList<>();
// 将字符串表达式转成一个list保存了某一个操作数和操作符方便后一步操作
List<String> normalExpression = transferExpressionStringToList();
// 从左至右扫描中缀表达式
for (String normalExp : normalExpression) {
// 如果是数字直接加入s1
if (normalExp.matches("\\d+")) {
s2.add(normalExp);
} else {
// 操作符和括号
// 括号处理
if (normalExp.equals("(")) {
// 左括号直接入栈
s1.push(normalExp);
} else if (normalExp.equals(")")) {
// 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
while (!s1.peek().equals("(")) {
s2.add(s1.pop());
}
// 消除左括号
s1.pop();
} else {
// 操作符处理
//如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
if (s1.isEmpty() || s1.peek().equals("(")) {
s1.push(normalExp);
} else if (getPriority(normalExp) > getPriority(s1.peek())) {
//若优先级比栈顶运算符的高,也将运算符压入s1
s1.push(normalExp);
} else {
// 否则,将s1栈顶的运算符弹出并压入到s2中,直到s1为空或者遇到一个比当前操作符优先级小的
while (!s1.isEmpty() && getPriority(normalExp) <= getPriority(s1.peek())) {
s2.add(s1.pop());
}
s1.push(normalExp);
}
}
}
}
// 把剩下所有s1中的操作符摊入s2
while (!s1.isEmpty()) {
s2.add(s1.pop());
}
polishExpressionList = s2;
}
public int getPriority(String str) {
return str.equals("*") || str.equals("/") ? 2 : 1;
}
/**
* 将一个中缀表达式 切分成数字和符号
*
* @return
*/
public List<String> transferExpressionStringToList() {
// 分割字符串
List<String> expList = new ArrayList<>();
int index = 0;
while (index < expression.length()) {
char c = expression.charAt(index);
String str = "";
// 多位数处理
if (c >= '0' && c <= '9') {
while (index < expression.length() && expression.charAt(index) >= '0' && expression.charAt(index) <= '9') {
str += expression.charAt(index);
index++;
}
} else {
str = c + "";
index++;
}
expList.add(str);
}
// 删除空白
return expList.stream().filter(s -> !"".equals(s.trim())).collect(Collectors.toList());
}
/**
* 中缀表达式计算方法
*
* @return
*/
public int calc() {
if (expression.trim().equals("") || null == polishExpressionList || polishExpressionList.size() == 0) {
throw new RuntimeException("empty expression");
}
Stack<String> calculator = new Stack<>();
for (String exp : polishExpressionList) {
if (exp.matches("\\d+")) {
//如果是数字直接入栈
calculator.push(exp);
} else if ("*".equals(exp)) {
Integer num1 = Integer.parseInt(calculator.pop());
Integer num2 = Integer.parseInt(calculator.pop());
calculator.push((num1 * num2) + "");
} else if ("+".equals(exp)) {
Integer num1 = Integer.parseInt(calculator.pop());
Integer num2 = Integer.parseInt(calculator.pop());
calculator.push((num1 + num2) + "");
} else if ("-".equals(exp)) {
Integer num1 = Integer.parseInt(calculator.pop());
Integer num2 = Integer.parseInt(calculator.pop());
calculator.push((num2 - num1) + "");
} else if ("/".equals(exp)) {
Integer num1 = Integer.parseInt(calculator.pop());
Integer num2 = Integer.parseInt(calculator.pop());
calculator.push((num2 / num1) + "");
} else {
throw new UnsupportedOperationException("error operator " + exp);
}
}
// 返回最终结果
return calculator.isEmpty() ? 0 : Integer.parseInt(calculator.peek());
}
public static void main(String[] args) {
PolandCalculator calculator = new PolandCalculator("1 + ( ( 2 + 3 )* 4) - 5");
int result = calculator.calc();
System.out.println(result);
}
}