中缀表达式转为后缀表达式 是实现逆波兰计算器的核心算法,其使用 栈(stack) 这一数据结构实现。本文转化算法(java版本)支持 整数和括号 的计算表达式转换。
1 前、中、后缀表达式
前缀表达式、中缀表达式、后缀表达式都是四则运算的表达方式,用以四则运算表达式求值。
- 中缀表达式:就是平常我们写的表达式。如1-(2+3) 和 1+((2+3)*4)-5。
- 前缀表达式(波兰式):通俗来说就是运算符写在前面,操作数写在后面。如 1+((2+3)*4)-5 对应的前缀表达式为 - + 1 * + 2 3 4 5 中缀转前缀也有一套算法。(前缀表达式介绍)
- 后缀表示式(逆波兰式):运算符位于操作数之后,本文重点介绍转化算法。
2 中缀转后缀表达式算法
2.1 转化算法伪代码(由波兰数学家提出)
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
- 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
- 否则,若优先级比栈顶运算符的高,也将运算符压入s1;
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较;
- 遇到括号时:
- 如果是左括号“(”,则直接压入s1;
- 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
- 重复步骤2至5,直到表达式的最右边;
- 将s1中剩余的运算符依次弹出并压入s2;
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式。
2.2 具体示例 1+((2+3)×4)-5 转换步骤
扫描到的元素 | s2 (栈底-栈顶) | s1 (栈底->栈顶) | 说明 |
---|---|---|---|
1 | 1 | 空 | 数字,直接入栈 |
+ | 1 | + | s1为空,运算符直接入栈 |
( | 1 | + | ( 左括号,直接入栈 |
( | 1 | + ( ( | 左括号,直接入栈 |
2 | 1 2 | + ( ( | 数字,直接入栈 |
+ | 1 2 | + ( ( + | s1栈顶为左括号,运算符直接入栈 |
3 | 1 2 3 | + ( ( + | 数字,直接入栈 |
) | 1 2 3 + | + ( | 右括号,弹出运算符直至遇到左括号 |
× | 1 2 3 + | + ( × | s1栈顶为左括号,运算符直接入栈 |
4 | 1 2 3 + 4 | + ( × | 数字,直接入栈 |
) | 1 2 3 + 4 × | + | 右括号,弹出运算符直至遇到左括号 |
- | 1 2 3 + 4 × + | - | -与+优先级相同,因此弹出+,再压入- |
5 | 1 2 3 + 4 × + 5 | - | 数字 |
到达最右端 | 1 2 3 + 4 × + 5 - | 空 | s1中剩余的运算符 |
因此结果为 :1 2 3 + 4 × + 5 -
2.3 Java算法代码
package stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* created by hbl on 2020/3/16.
*/
public class PolandNotation {
public static void main(String[] args) {
String expression = "1+((2+3)*4)-5";
List<String> infixList = string2List(expression);
List<String> suffixList = infixList2SuffixList(infixList);
int res = calculate(suffixList);
System.out.printf("表达式 %s=%d", expression, res);
}
/**
* 将中缀表达式转换为后缀表达式.
* 特别注意:因为存储中间结果的栈在算法操作过程中没有使用pop操作,并且最后还要逆序输出,
* 我将其改为ArrayList进行存储,这样也不用逆序输出了。
*
* @param infixList 中缀表达式组成的List
* @return 后缀表达式的list
*/
private static List<String> infixList2SuffixList(List<String> infixList) {
Stack<String> stack = new Stack<>(); //符号栈s1
ArrayList<String> list = new ArrayList<>(); // 中间结果栈s2
for (String item : infixList) {
if (item.matches("\\d+")) {
list.add(item);
} else if (item.equals("(")) {
stack.push(item);
} else if (item.equals(")")) {
while (!stack.peek().equals("(")) {
list.add(stack.pop());
}
stack.pop();
} else {
if (stack.size() != 0 && getValue(item) <= getValue(stack.peek())) {
while (stack.size() != 0 && getValue(item) <= getValue(stack.peek())) {
list.add(stack.pop());
}
}
stack.push(item);
}
}
while (stack.size() != 0) {
list.add(stack.pop());
}
return list;
}
private static int getValue(String item) {
if (item.equals("+") || item.equals("-")) {
return 1;
} else if (item.equals("*") || item.equals("/")) {
return 2;
} else {
return 0; //算法中如果(主算法中括号则会直接放进去,这里是查看栈中的优先级,
//而栈中'('是没有优先级的,因为栈顶是(则直接放进去操作符。
}
}
private static List<String> string2List(String expression) {
ArrayList<String> list = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (c < 48 || c > 57) {
list.add("" + c);
} else {
sb.append(c);
if (i == expression.length() - 1) {
list.add(sb.toString());
} else {
char cNext = expression.charAt(i + 1);
if (cNext < 48 || cNext > 57) {
list.add(sb.toString());
sb.setLength(0);
}
}
}
}
return list;
}
private static int calculate(List<String> list) {
Stack<Integer> stack = new Stack<>();
for (String item : list) {
if (item.matches("\\d+")) {
stack.push(Integer.valueOf(item));
} else {
int num1 = stack.pop();
int num2 = stack.pop();
int res = getRes(num1, num2, item);
stack.push(res);
}
}
return stack.pop();
}
private static int getRes(int num1, int num2, String item) {
int res = 0;
switch (item) {
case "+":
res = num1 + num2;
break;
case "-":
res = num2 - num1;
break;
case "*":
res = num1 * num2;
break;
case "/":
res = num2 / num1;
break;
}
return res;
}
}
如发现错误或不足,请评论告知。
如果觉得有用,请点个赞吧!蟹蟹~~