前言
中缀表达式就是我们在进行加减乘除时经常使用的表达式,后缀表达式是对计算机友好的表达式,计算机可以利用两个栈来实现输入的计算字符串的计算工作
提示:以下是本篇文章正文内容,下面案例可供参考
一、后缀表达式
后缀表达式也叫逆波兰式,实现逆波兰式的算法,难度并不大,但为什么要将看似简单的中缀表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
(a+b)c-(a+b)/e的后缀表达式为:ab+cab+e/-
二、中缀转后缀 方式
首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为存放结果(逆波兰式)的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
- 若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈。
- 若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符(不包括括号运算符)优先级高于S1栈栈顶运算符(包括左括号)优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符(包括左括号)低于(不包括等于)该运算符优先级时停止弹出运算符,最后将该运算符送入S1栈。
- 若取出的字符是“(”,则直接送入S1栈顶。
- 若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
- 重复上面的1~4步,直至处理完所有的输入字符。
- 若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
计算示例
三、中缀 转 后缀 并计算 实现
public class testPolan {
public static void main(String[] args) {
String exp = "1+((12+0)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(exp);
List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println("后缀List" + suffixExpressionList);
System.out.printf(exp + " = " + calculate(suffixExpressionList));
}
//方法:将中缀转后缀
public static List<String> parseSuffixExpressionList(List<String> ls) {
//定义两个栈
Stack<String> s1 = new Stack<String>();//符号栈
//因为s2这个栈没有pop,后面还要逆序输出
//因此比较麻烦,我们直接使用List2
List<String> s2 = new ArrayList<String>();
//遍历ls
for (String item : ls) {
//如果是一个数就加入s2
if (item.matches("\\d+")) {
s2.add(item);
} else if (item.equals("(")) {
s1.push(item);
} else if (item.equals(")")) {
while (!s1.peek().equals("(")) {
s2.add(s1.pop());
}
s1.pop();//将小括号弹出
} else {
//item的运算符优先级小于等于栈顶
//缺少优先级方法,见下
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
s2.add(s1.pop());
}
//还需要将item压入栈中
s1.push(item);
}
}
//将s1剩余的加入s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
return s2;
}
//方法:将中缀表达式转成对应的List
public static List<String> toInfixExpressionList(String s) {
//先定义一个list存放中缀表达式对应的数据
List<String> ls = new ArrayList<String>();
int i = 0;//这是一个指针,用于遍历中缀表达式字符串
String str;//多位数的拼接
char c;//每遍历一个字符就放到c中
do {
//如果c是非数字,我们就加到ls
if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {
ls.add("" + c);
i++;
} else {
//考虑多位数
str = "";
while (i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57) {
str += c;
i++;
}
ls.add(str);
}
} while (i < s.length());
return ls;
}
//从左往右扫描表达式,遇到数字时,将数字压入堆栈
//遇到运算符时,弹出栈顶的两个数,计算
//注意顺序:次顶元素 -处理->栈顶元素
//并将结果入栈,重复上述过程直到表达式最右端
//最后结果即为计算结果
public static int calculate(List<String> ls) {
Stack<String> stack = new Stack<String>();
//遍历ls
for (String item : ls) {
if (item.matches("\\d+")) {
//入栈
stack.push(item);
} else {
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")) {
res = num1 + num2;
} else if (item.equals("-")) {
res = num1 - num2;
} else if (item.equals("*")) {
res = num1 * num2;
} else if (item.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("错误");
}
stack.push(res + "");
}
}
return Integer.parseInt(stack.pop());
}
//类:返回优先级数字
static class Operation {
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
//方法,返回对应的优先数字
public static int getValue(String oper) {
int result = 0;
switch (oper) {
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
default:
// System.out.println("无法解析");
break;
}
return result;
}
}
}
四、总结
加油噢!