后缀表达式 - 逆波兰

计算 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();
    }
}
posted @   小段的老公  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示