【数据结构与算法】中缀表达式转后缀表达式以及后缀表达式的计算
中缀表达式转后缀表达式
方式一
步骤
1️⃣ 如果遇到操作数,我们就直接将其输出。
2️⃣ 如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。
3️⃣ 如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。
4️⃣ 如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)(或发现最近的左括号)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) "的情况下我们才弹出" ( ",其他情况我们都不会弹出" ( "。
5️⃣ 如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
代码实现
package demo03;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
public class InfixToPostfixExpression {
private char[] expression;
private Deque<Character> stack;
private ArrayList<Character> list; // 保存答案
public InfixToPostfixExpression(char[] ex) {
this.expression = ex;
stack = new ArrayDeque<>();
list = new ArrayList<>();
}
public ArrayList process() {
for (char c : expression) {
if (isOperand(c)) { //如果是操作数,入集合
list.add(c);
}
else if (isBracket(c)) { // 如果是括号
if (isLeftBracket(c)) { // 如果是左括号,入栈
stack.addLast(c);
} else { // 如果是右括号
while (!stack.isEmpty() && stack.peekLast() != '(') { // 一直弹出栈中元素知道遇到左括号(左括号也要但还没弹出)
list.add(stack.removeLast());
}
if (!stack.isEmpty()) stack.removeLast(); //移除栈中匹配的左括号
}
} else if (isOperator(c)) { // 如果 c 是操作符
if (stack.isEmpty()) { // 如果栈空,则入栈
stack.addLast(c);
}
else {
// 计算优先值(0:相等,-1:c优先值低于栈顶,1:c优先值大于栈顶)
int priority = priority(c) - priority(stack.peekLast());
if (priority < 0) { //遇到优先度低的,一直弹出来(直到遇到左括号)
while (!stack.isEmpty() && !isLeftBracket(stack.peekLast())) {
list.add(stack.removeLast());
}
}
stack.addLast(c); // 最后 c 入栈
}
}
}
while (!stack.isEmpty()) { // 栈中剩余元素都弹出并入集合
list.add(stack.removeLast());
}
return list;
}
// 判断是否是操作数
private boolean isOperand(char c) {
return !(isOperator(c) || isBracket(c));
}
// 计算优先值
private int priority(char c) {
if (c == '+' || c == '-') return 0;
else return 1; //if (c == '*' || c == '/')
}
// 判断是否是左括号
private boolean isLeftBracket(char c) {
return c == '(';
}
// 判断是否是右括号
private boolean isRightBracket(char c) {
return c == ')';
}
// 判断是否是操作符
public boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
// 判断是否是括号
public boolean isBracket(char c) {
return c == '(' || c == ')';
}
}
测试
public static void main(String[] args) {
String ex = "a + b * c + (d * e + f)*g";
ex = ex.trim().replace(" ", "");
char[] e = ex.toCharArray();
InfixToPostfixExpression expression = new InfixToPostfixExpression(e);
ArrayList process = expression.process();
System.out.println(process.toString());
// 输出:[a, b, c, *, +, d, e, *, f, +, g, *, +]
}
方式二
1️⃣ 先按照运算符的优先级对中缀表达式加括号,变成((a + (b * c)) + (((d * e) + f) * g ))
2️⃣ 将运算符移到括号的后面,变成((a(bc) * )+(((de) * f) + g) * ) +
3️⃣ 去掉括号,得到 abc *+ de * f + g *+
后缀表达式的计算
后缀表达式也叫逆波兰表达式。
逆波兰表达式严格遵循「从左到右」的运算。计算逆波兰表达式的值时,使用一个栈存储操作数,从左到右遍历逆波兰表达式,进行如下操作:
如果遇到操作数,则将操作数入栈;
如果遇到运算符,则将两个操作数出栈,其中先出栈的是右操作数,后出栈的是左操作数,使用运算符对两个操作数进行运算,将运算得到的新操作数入栈。
整个逆波兰表达式遍历完毕之后,栈内只有一个元素,该元素即为逆波兰表达式的值。
代码
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList<>();
int n = tokens.length;
for(int i = 0;i<n;i++){
if(isNum(tokens[i])){
stack.push(Integer.parseInt(tokens[i]));
}else{
int b = stack.pop();
int a = stack.pop();
switch(tokens[i]){
case "+":{
stack.push(a+b);
break;
}
case "-":{
stack.push(a-b);
break;
}
case "*":{
stack.push(a*b);
break;
}
case "/":{
stack.push(a/b);
break;
}
}
}
}
return stack.pop();
}
public boolean isNum(String s){
return !(("+".equals(s))||("-".equals(s))||("*".equals(s))||("/".equals(s)));
}
}
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:
该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22