后缀表达式 - 逆波兰
计算 a+b*(c-d)+e/f 的值?
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
/**
* 表达式解析
* <p>
* 1. 前缀表达式,操作符在操作数的前面,比如 +ab
* <br>
* 2. 中缀表达式,操作符在操作数的中间,比如 a+b
* <br>
* 3. 后缀表达式,操作符在操作数的后面,比如 ab+
* <p>
* 可以看出,中缀表达式是最直观、最符合人的理解与计算,但是括号提升优先级等比较复杂。
* 后缀表达式可以没有界限符的情况下无歧义的计算表达式的值,因此在读取一个中缀表达式后,我们将他转化为后缀表达式。
* <p>
* 所有复杂的操作,最终都可以简化为两个操作符之间的计算。
* <p>
* a + b * (c - d) / e
* <br>
* 中缀转后缀,主要工具为栈S,S存放运算符,整个过程从前往后遍历中缀表达式:
* <br>
* 1. 当遇到操作数时,直接将其加入到结果序列中。
* <br>
* 2. 当遇到左括号时,将左括号入栈。
* <br>
* 3. 当遇到右括号时,将栈中左括号 之前 的所有操作符弹出,并加入到结果序列中。同时将左括号弹出。
* <br>
* 4. 当遇到运算符时,若栈为空,将当前操作符入栈。否则将其与栈顶运算符进行比较,若当前操作符优先级高于栈顶,则将当前操作符入栈。
* 否则将栈顶弹出,加入到结果序列中,重复此过程。
* <br>
* 5. 当遍历完整个中缀表达式后,栈中剩余的运算符依次弹出加入到结果序列中。
*
* @author administration
* @version 1.0
*/
public class ExpressionParsing {
public static void main(String[] args) {
/*
* a+b*(c-d)+e/f
* 1 + 2 * (3 - 4) + 1 = 0
*/
String expression = "a+b*(c-d)+e/f";
HashMap<Character, Integer> map = new HashMap<>();
map.put('a', 1);
map.put('b', 2);
map.put('c', 3);
map.put('d', 4);
map.put('e', 5);
map.put('f', 5);
System.out.println("中缀表达式:" + expression);
String suffix = convertSuffix(expression.toCharArray());
System.out.println("后缀表达式:" + suffix);
int evaluate = evaluate(suffix, map);
System.out.println("后缀表达式计算结果:" + evaluate);
}
public static final List<Character> OPERATORS = List.of('+', '-', '*', '/', '(', ')');
/**
* 获取运算符的优先级
* + - 的优先级低于 * /
*
* @param c 运算符
* @return 优先级
*/
public static int Priority(char c) {
return switch (c) {
case '+', '-' -> 1;
case '*', '/' -> 2;
default -> 0;
};
}
/**
* 将中缀表达式转换为后缀表达式
*
* @param infix 中缀表达式
* @return 后缀表达式
*/
public static String convertSuffix(char[] infix) {
// 后缀表达式
StringBuilder suffix = new StringBuilder();
// 符号栈
Stack<Character> stack = new Stack<>();
for (char letter : infix) {
// 获取当前字符
// 非运算符,直接添加到后缀表达式中
if (!OPERATORS.contains(letter)) {
suffix.append(letter);
continue;
}
// 如果栈为空,或者当前字符是 "(",或者 letter 的优先级大于栈顶的优先级,直接入栈
if (stack.empty() || letter == '(' || Priority(letter) > Priority(stack.peek())) {
stack.push(letter);
continue;
}
// 如果当前字符是 ")", 则循环将栈顶的运算符弹出,直到遇到 "(",且栈是非空的
if (letter == ')') {
while (!stack.empty() && stack.peek() != '(') {
suffix.append(stack.pop());
}
// 此时,栈顶的元素应该是 "(",弹出
stack.pop();
continue;
}
// 如果当前字符的优先级小于栈顶的优先级,则将栈顶的运算符弹出,直到栈顶的优先级小于当前字符的优先级,然后将当前字符入栈
while (!stack.isEmpty() && Priority(letter) <= Priority(stack.peek())) {
suffix.append(stack.pop());
}
// 将当前字符入栈
stack.push(letter);
}
// 将栈中剩余的运算符弹出,添加到后缀表达式中
while (!stack.isEmpty()) {
suffix.append(stack.pop());
}
return suffix.toString();
}
/**
* 后缀表达式计算
* 加法、乘法:两个操作数相加、乘,交换律不影响最终结果
* 减法、除法:两个操作数相减、除,需要注意减法和除法的操作数顺序问题,栈是先入后出的
* ab- 对应 a-b,a在栈底,b在栈顶
*
* @param suffix 后缀表达式
* @param map 表达式对应的值
* @return 运算结果
*/
public static int evaluate(String suffix, HashMap<Character, Integer> map) {
Stack<Integer> stack = new Stack<>();
char[] charArray = suffix.toCharArray();
for (char token : charArray) {
switch (token) {
case '+' -> {
int operand1 = stack.pop();
int operand2 = stack.pop();
stack.push(operand2 + operand1);
}
case '-' -> {
int operand1 = stack.pop();
int operand2 = stack.pop();
stack.push(operand2 - operand1);
}
case '*' -> {
int operand1 = stack.pop();
int operand2 = stack.pop();
stack.push(operand2 * operand1);
}
case '/' -> {
int operand1 = stack.pop();
int operand2 = stack.pop();
stack.push(operand2 / operand1);
}
default -> stack.push(map.get(token));
}
}
return stack.pop();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧