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