中缀表达式转后缀表达式,Java实现
中缀表达式是一个通用的算术或逻辑公式表示方法,操作符是以中缀形式处于操作数的中间(例:3+4),中缀表达式是人们常用的算术表示方法。
与前缀表达式(例:+34)或后缀表达式(例:34+)相比,中缀表达式不容易被计算机解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。
例如:
例1:8 + 4 - 6 * 2 用后缀表达式表示为:
8 4 + 6 2 * -
例2:2 * ( 3 + 5 ) + 7 / 1 - 4 用后缀表达式表示为:
2 3 5 + * 7 1 / + 4 - ***百科的解【3 5 + 2 * 7 1 / 4 - +】是错误的,虽然结果正确但数字顺序一般不会变化
——转自百度百科
中缀转后缀是如何转换的网上已经讲解的很详细了,本文不做赘述。
如果不理解的同学可以看:https://www.bilibili.com/video/av18586085/?p=20
代码实现:
public class Main {
public static void main(String[] args) {
Stack<String> stack = new Stack<>(); // new一个栈
String[] array = "2 * ( 9 + 6 / 3 - 5 ) + 4".split(" "); // 中缀表达式
List<String> list = new LinkedList<>(); // list存储后缀表达式
for (String s : array) {
if (isNumber(s)) {
// 数字直接输出
list.add(s);
} else {
// 1.根据当前读到的运算符判断是否需要弹出
// 1.1 如果栈当前不为空且s不是"("且栈顶不是"(",则判断运算符
// 反之:栈为空栈或当前字符为"("或栈顶是"(",则s直接入栈
if (stack.size() > 0 && !"(".equals(s) && !"(".equals(stack.peek())) {
// 如果是")",将栈顶至第一个"("弹出
String top = null;
if (")".equals(s)) {
while (!"(".equals(top = stack.pop())) {
list.add(top);
}
} else {
// 如果是运算符,比较优先级,将优先级大于等于当前s的都弹出
while (stack.size() > 0 && compare(top = stack.peek(), s) > -1) {
list.add(top);
stack.pop();
}
}
}
// 2.把当前运算符压入栈
if (!")".equals(s)) {
stack.push(s);
}
}
}
// 3.最后栈中剩的一定是最后一个运算符和比最后一个运算符优先级小的运算符,否则之前就已经弹出了
while (stack.size() > 0) {
list.add(stack.pop());
}
// 4.输出后缀表达式
for (String s : list) {
System.out.print(" " + s);
}
System.out.println();
}
// 比较两个运算符,返回正数则a优先级大于b,负数则a优先级小于b
private static int compare(String a, String b) {
// "("的优先级比任何运算符都小
if ("(".equals(a)) {
return -99999;
}
if ("(".equals(b)) {
return 99999; // 本实现中b不会出现这种情况
}
int aScore = "+".equals(a) || "-".equals(a) ? 0 : 1;
int bScore = "+".equals(b) || "-".equals(b) ? 0 : 1;
return aScore - bScore;
}
// 是数字则返回true
private static isNumber(String s){
return "0123456789".contains(s);
}
}
说一下我自己的一点理解:
-
后缀表达式的
优先级是众生平等
的,运算顺序就是从左至右,即使加减在乘除之前,也需要先算加减 -
因为后缀表达式的数字顺序是不会变化的,
仅运算符变位
,所以没必要完全遵循人直观的理解将乘除或者括号里的数字放到前面(就如百度百科错误的解法一样),只要保证一个运算符
是计算前面两个元素的结果即可 -
所以需要保证所有运算符都入一次栈,这样就能保证优先级高的先出栈,然后再把此次的运算符入栈,不用担心出现例1中
+
先弹出*
后弹出会不会出错,可以这样理解:因为这是一个二元运算,在判断
*
入栈时+
已经出栈,而它出栈是因为-
,说明前面至少已经有3个数字了,那么即使做一次+
运算前面也只是头2个数字,而*
现在只需要前1个数字,所以这次+
运算不会影响*
需要的数字,但是如果在+
之后紧接着的就是*
,*
入栈时如果做了一次+
就会对*
需要的数字有影响,所以+
不能出栈 -
在没有括号的表达式中,乘除的优先级高于加减没什么可说,但乘除的计算也是从左到右,相当于
同级运算符也是有优先级的
,在一个乘除前面的乘除一定会先输出,所以出栈的时候要把这个比这个运算符优先级高的或相同优先级
的都弹出 -
括号是一个bug,相当于
被括号包围的运算符优先级翻倍
,所以即使是加减在括号内也比括号外的乘除优先级高的多,那么在代码里如何体现优先级翻倍,自然不能在compare方法中让b翻倍,那么就把(
的优先级设到最低,让(
无论如何都不会弹出,让它等)
的出现就好