06-逆波兰表达式

作者: 紫荆秋雪_文 | 来源:发表于2022-06-22 16:30 被阅读0次

    铭记:

    \color{#DC143C}{算法 + 数据结构 = 程序}

    源码下载

    一、逆波兰表达式(后缀表达式)

    (3+4)*5-6 对应的后缀表达式就是 3 4 + 5 * 6 -

    1、逆波兰表达式计算规则

    • 1、从左到右扫描,将 3 和 4 压入堆栈
    • 2、遇到 + 运算符,因此弹出 4 和 3 ,计算出 3 + 4 = 7,在将 7 压入栈
    • 3、将 5 入栈
    • 4、接下来是 * 运算符,因此弹出 5 和 7,计算出 7 * 5 = 35,将 35 压栈
    • 5、将 6 入栈
    • 6、最后是 - 运算符,计算出 35 - 6 = 29,最后把 29 压入栈

    2、中缀表达式 转换为 逆波兰表达式(后缀表达式)

    后缀表达式:适合计算式进行运算,但是不适合人的书写;
    中缀表达式:适合人的书写,但是不适合计算式运算;
    所以需要把 [中缀表达式] 转为 [后缀表达式]

    思路:

    • 1、初始化两个栈:运算符栈(s1)和 存储中间结果的栈(s2)
    • 2、从左到右扫描中缀表达式
    • 3、遇到操作数时,将其压s2
    • 4、遇到运算符时,与 s1 栈顶的运算符比较优先级
      • 4.1、如果 s1 为空,或栈顶运算符为左括号"(",则直接将此运算符入栈
      • 4.2、否则,若优先级比栈顶运算符的高,也将运算符压入s1
      • 4.3、否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到 4.1 与 s1 中新的栈顶运算符相比较
    • 5、遇到括号时
      • 5.1、如果是左括号"(",则直接压入s1
      • 5.2、如果是右括号")",则依次弹出 s1 栈顶的运算符,并压入 s2 ,直到遇到左括号为止,此时将这一对括号丢弃
    • 6、重复步骤 2~5,直到表达式的最右侧
    • 7、将 s1 中剩余的运算符依次弹出并压入 s2
    • 8、依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

    二、代码 PolandNotation

    package com.raven.alg.s4stack;
    
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Stack;
    
    /**
     * 逆波兰计算器
     * <p>
     * 例如:(3+4)*5-6  对应的后缀表达式就是 3 4 + 5 * 6 -
     * 1、从左到右扫描,将 3 和 4 压入堆栈
     * 2、遇到 + 运算符,因此弹出 4 和 3 ,计算出 3 + 4 = 7,在将 7 压入栈
     * 3、将 5 入栈
     * 4、接下来是 * 运算符,因此弹出 5 和 7,计算出 7 * 5 = 35,将 35 压栈
     * 5、将 6 入栈
     * 6、最后是 - 运算符,计算出 35 - 6 = 29,最后把 29 压入栈
     * <p>
     * 这就是后缀表达式计算思路,但是如何把 [中缀表达式] 转为 [后缀表达式]
     * <p>
     * [中缀表达式] 转为 [后缀表达式]
     * 后缀表达式:适合计算式进行运算,但是不适合人的书写
     * 中缀表达式:适合人的书写,但是不适合计算式运算
     * 所以需要把 [中缀表达式] 转为 [后缀表达式]
     * 思路:
     * 1、初始化两个栈:运算符栈(s1)和 存储中间结果的栈(s2)
     * 2、从左到右扫描中缀表达式
     * 3、遇到操作数时,将其压s2
     * 4、遇到运算符时,与 s1 栈顶的运算符比较优先级
     * 4.1、如果 s1 为空,或栈顶运算符为左括号"(",则直接将此运算符入栈
     * 4.2、否则,若优先级比栈顶运算符的高,也将运算符压入s1
     * 4.3、否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到 4.1 与 s1 中新的栈顶运算符相比较
     * 5、遇到括号时
     * 5.1、如果是左括号"(",则直接压入s1
     * 5.2、如果是右括号")",则依次弹出 s1 栈顶的运算符,并压入 s2 ,直到遇到左括号为止,此时将这一对括号丢弃
     * 6、重复步骤 2~5,直到表达式的最右侧
     * 7、将 s1 中剩余的运算符依次弹出并压入 s2
     * 8、依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
     */
    public class PolandNotation {
    
    
        /**
         * 计算器
         *
         * @param poland
         * @return
         */
        public static Integer calculatorPoland(List<String> poland) {
            return Operation.calculator(poland);
        }
    
    
        /**
         * 例如:(3+4)*5-6  对应的后缀表达式就是 3 4 + 5 * 6 -
         * [中缀表达式] 转为 [后缀表达式]
         * 后缀表达式:适合计算式进行运算,但是不适合人的书写
         * 中缀表达式:适合人的书写,但是不适合计算式运算
         * 所以需要把 [中缀表达式] 转为 [后缀表达式]
         * 思路:
         * 1、初始化两个栈:运算符栈(s1)和 存储中间结果的栈(s2)
         * 2、从左到右扫描中缀表达式
         * 3、遇到操作数时,将其压s2
         * 4、遇到运算符时,与 s1 栈顶的运算符比较优先级
         * 4.1、如果 s1 为空,或栈顶运算符为左括号"(",则直接将此运算符入栈
         * 4.2、否则,若优先级比栈顶运算符的高,也将运算符压入s1
         * 4.3、否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到 4.1 与 s1 中新的栈顶运算符相比较
         * 5、遇到括号时
         * 5.1、如果是左括号"(",则直接压入s1
         * 5.2、如果是右括号")",则依次弹出 s1 栈顶的运算符,并压入 s2 ,直到遇到左括号为止,此时将这一对括号丢弃
         * 6、重复步骤 2~5,直到表达式的最右侧
         * 7、将 s1 中剩余的运算符依次弹出并压入 s2
         * 8、依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
         */
        public static List<String> parsePoland(String ls) {
            // 运算符栈(s1)
            Stack<String> s1 = new Stack<>();
            // 存储中间结果的栈(s2)
            List<String> list = new ArrayList<>();
            // 去除空格
            String trim = ls.trim();
            Integer index = 0;
            // 拼接多位数
            StringBuilder numBuilder = null;
    
            while (index < trim.length()) {
                String sub = trim.substring(index, index + 1);
                System.out.println("index = " + index + " sub = " + sub);
    
                // 3、遇到操作数时,将其压s2
                if (sub.matches("\\d+")) {
                    numBuilder = new StringBuilder("");
                    while (sub.matches("\\d+") && index < trim.length()) {
                        numBuilder.append(sub);
                        index++;
                        if (index < trim.length()) {
                            sub = trim.substring(index, index + 1);
                        }
                    }
                    list.add(numBuilder.toString());
                    // 减去 while 中的 ++
                    index--;
                }
                // 4、遇到运算符时,与 s1 栈顶的运算符比较优先级
                else if (Operation.isOperation(sub)) {
                    while (true) {
                        // 4.1、如果 s1 为空,或栈顶运算符为左括号"(",则直接将此运算符入栈
                        if (s1.isEmpty() || s1.peek().equals("(")) {
                            s1.push(sub);
                            break;
                        }
                        // 4.2、否则,若优先级比栈顶运算符的高,也将运算符压入s1
                        else if (Operation.priority(sub) > Operation.priority(s1.peek())) {
                            s1.push(sub);
                            break;
                        }
                        // 4.3、否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到 4.1 与 s1 中新的栈顶运算符相比较
                        else {
                            list.add(s1.pop());
                        }
                    }
                }
                // 5、遇到括号时
                // 5.1、如果是左括号"(",则直接压入s1
                else if (sub.equals("(")) {
                    s1.push(sub);
                }
                // 5、遇到括号时
                // 5.2、如果是右括号")",则依次弹出 s1 栈顶的运算符,并压入 s2 ,直到遇到左括号为止,此时将这一对括号丢弃
                else if (sub.equals(")")) {
                    while (!s1.peek().equals("(")) {
                        list.add(s1.pop());
                    }
                    // 丢弃 s1 的左括号
                    s1.pop();
                }
                index++;
            }
    
    
            // 7、将 s1 中剩余的运算符依次弹出并压入 s2
            while (!s1.isEmpty()) {
                list.add(s1.pop());
            }
    
            // 8、依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
    
            return list;
        }
    
    }
    
    
    /**
     * 操作
     */
    class Operation {
    
        /**
         * 判断是否为操作符
         */
        public static Boolean isOperation(String item) {
            return "+".equals(item) || "-".equals(item) || "*".equals(item) || "/".equals(item);
        }
    
    
        /**
         * 判断操作符的优先级
         */
        public static int priority(String item) {
            switch (item) {
                case "+":
                case "-":
                    return 1;
    
                case "*":
                case "x":
                case "/":
                    return 2;
                default:
                    throw new RuntimeException("操作符错误");
            }
        }
    
    
        /**
         * 判断操作符的优先级
         * 3 4 + 5 * 6 -
         */
        public static Integer calculator(List<String> poland) {
            Integer res = 0;
            Stack<String> stack = new Stack<>();
            for (String item : poland) {
                // 如果是数字
                if (item.matches("\\d+")) {
                    stack.push(item);
                } else {
                    int num1 = Integer.parseInt(stack.pop());
                    int num2 = Integer.parseInt(stack.pop());
    
                    switch (item) {
                        case "+":
                            res = num1 + num2;
                            break;
                        case "-":
                            res = num2 - num1;
                            break;
                        case "*":
                        case "x":
                            res = num1 * num2;
                            break;
                        case "/":
                            res = num2 / num1;
                            break;
                        default:
                            throw new RuntimeException("操作符错误");
                    }
                    // 结果入栈
                    stack.push(res.toString());
                }
            }
            return res;
        }
    }
    
    

    相关文章

      网友评论

        本文标题:06-逆波兰表达式

        本文链接:https://www.haomeiwen.com/subject/fzduvrtx.html