6.基于stack实现简单的逆波兰计算器

波兰表达式

我们常见人类容易理解的计算公式如1+2 (3+2)等属于中缀表达式,这种表达式的特点是如何人们阅读的习惯,但是计算机在处理的时候需要对公式进行运算顺序的处理,不利于计算机的理解,从而计算机学家发明了波兰表达式这种易于计算机理解的表达式。

  1. 前缀表达式,又称波兰表达式,特点在于运算符在前操作数在后如 - + * 1 2 3 4,计算顺序从右至左,
    1. 依次将3 2 1 压入stack
    2. 遇操作符 pop出stack中的1 2 计算 得到 2 重新push到stack中
    3. 遇+操作符 弹出 stack中 2 3 计算 得到 5 重新push到stack中
    4. 遇-操作符 弹出 5 和 4 又栈顶为被减数 即 5-4 重新push到stack中
    5. 最后stack只有一个数为计算结果
  2. 中缀表达式 即为我们普通常用的计算公式
  3. 后缀表达式,又称逆波兰表达式,因为前缀表达式并不如何人类的阅读习惯,所有出现了后缀表达式,从左往右依次计算,最终堆栈的最后一个数即为结果
    1. 例如 1 + ( ( 2 + 3 )* 4) - 5 转为逆波;兰表达式为 1 2 3 + 4 * + 5 -
    2. 后缀表达式的计算规则为如遇到数字直接push到堆栈如遇到操作符从堆栈中弹出2个数计算完之后重新压入堆栈直到堆栈只有一个数
      1. 逆波兰表达式计算规则 1 2 3 数字 压入 堆栈
      2. 遇到+号操作符 弹出2个数字 3 2 计算得到 5 压入堆栈 其次堆栈从栈底到栈顶依次为 1 5
      3. 遇到数字4压入 1 5 4
      4. 遇到*号操作符 弹出 4 5 计算 20 压入 此时为 1 20
      5. 遇到+号 弹出 20 1 计算 21 压入 21
      6. 遇到数字5 压入 21 5
      7. 遇到-号操作符 弹出 5 21后面一个数减去前面一个数即 21 - 5 得到16
    3. 中缀表达式转后缀表达式
      1. 首先初始化两个栈stack,运算符栈stack1 和中间结果栈stack s2
      2. 从左往右开始扫描中缀表达式
      3. 遇到数字直接压入 s2
      4. 遇到操作符需要做特殊处理
        1. 如果是左括号( 直接压入s1
        2. 如果是右括号 ) 需要从左边s1中依次弹出若干操作符压入s2 直到遇到 左括号)为止,并且最后叫匹配的左括号弹出 即消除了一对括号
        3. 如果是正常操作符
          1. 如果当前操作符的优先级大于 s1中栈顶的优先级 则直接压入s1
          2. 如果s1为空 或者 s1的栈顶为 左括号( 直接压入s1
          3. 如果当前操作符优先级小于等于 s1栈顶操作符的优先级,则从s1的栈顶弹出操作符并压入到s2中,一直循环操作第三步,即一直进行到s1为空或者s1栈顶的操作符优先级小于了当前操作符,最终将当前操作符压入到s1中
      5. 最终将s1中剩余的所有操作符依次弹出压入s2
      6. 最终将s2的栈数据反转即(从栈底到栈顶)得到中缀表达式

代码实现

public class PolandCalculator {

    // 构造器传入的中缀表达式
    public final String expression;

    // 用于保存解析出来的后缀表达式
    public List<String> polishExpressionList;

    /**
     * 中缀表达式
     *
     * @param expression
     */
    public PolandCalculator(String expression) {
        this.expression = expression;
        init();
    }

    /**
     * 中缀表达式转后缀表达式的方法
     */
    public void init() {
        // 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
        Stack<String> s1 = new Stack<>();
        // 因为s2中没有任何pop的操作 即用list处理更加方法,而且也省去了stack反转的麻烦,因为list本身就是顺序添加的,不需要像stack一样先进后出
        ArrayList<String> s2 = new ArrayList<>();
        // 将字符串表达式转成一个list保存了某一个操作数和操作符方便后一步操作
        List<String> normalExpression = transferExpressionStringToList();
        // 从左至右扫描中缀表达式
        for (String normalExp : normalExpression) {
            // 如果是数字直接加入s1
            if (normalExp.matches("\\d+")) {
                s2.add(normalExp);
            } else {
                // 操作符和括号
                // 括号处理
                if (normalExp.equals("(")) {
                    // 左括号直接入栈
                    s1.push(normalExp);
                } else if (normalExp.equals(")")) {
                    // 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
                    while (!s1.peek().equals("(")) {
                        s2.add(s1.pop());
                    }
                    // 消除左括号
                    s1.pop();
                } else {
                    // 操作符处理
                    //如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
                    if (s1.isEmpty() || s1.peek().equals("(")) {
                        s1.push(normalExp);
                    } else if (getPriority(normalExp) > getPriority(s1.peek())) {
                        //若优先级比栈顶运算符的高,也将运算符压入s1
                        s1.push(normalExp);
                    } else {
                        // 否则,将s1栈顶的运算符弹出并压入到s2中,直到s1为空或者遇到一个比当前操作符优先级小的
                        while (!s1.isEmpty() && getPriority(normalExp) <= getPriority(s1.peek())) {
                            s2.add(s1.pop());
                        }
                        s1.push(normalExp);
                    }
                }
            }
        }
        // 把剩下所有s1中的操作符摊入s2
        while (!s1.isEmpty()) {
            s2.add(s1.pop());
        }

        polishExpressionList = s2;
    }

    public int getPriority(String str) {
        return str.equals("*") || str.equals("/") ? 2 : 1;
    }

    /**
     * 将一个中缀表达式 切分成数字和符号
     *
     * @return
     */
    public List<String> transferExpressionStringToList() {
        // 分割字符串
        List<String> expList = new ArrayList<>();
        int index = 0;
        while (index < expression.length()) {
            char c = expression.charAt(index);
            String str = "";
            // 多位数处理
            if (c >= '0' && c <= '9') {
                while (index < expression.length() && expression.charAt(index) >= '0' && expression.charAt(index) <= '9') {
                    str += expression.charAt(index);
                    index++;
                }
            } else {
                str = c + "";
                index++;
            }
            expList.add(str);
        }
        // 删除空白
        return expList.stream().filter(s -> !"".equals(s.trim())).collect(Collectors.toList());
    }

    /**
     * 中缀表达式计算方法
     *
     * @return
     */
    public int calc() {
        if (expression.trim().equals("") || null == polishExpressionList || polishExpressionList.size() == 0) {
            throw new RuntimeException("empty expression");
        }

        Stack<String> calculator = new Stack<>();
        for (String exp : polishExpressionList) {
            if (exp.matches("\\d+")) {
                //如果是数字直接入栈
                calculator.push(exp);
            } else if ("*".equals(exp)) {
                Integer num1 = Integer.parseInt(calculator.pop());
                Integer num2 = Integer.parseInt(calculator.pop());
                calculator.push((num1 * num2) + "");
            } else if ("+".equals(exp)) {
                Integer num1 = Integer.parseInt(calculator.pop());
                Integer num2 = Integer.parseInt(calculator.pop());
                calculator.push((num1 + num2) + "");
            } else if ("-".equals(exp)) {
                Integer num1 = Integer.parseInt(calculator.pop());
                Integer num2 = Integer.parseInt(calculator.pop());
                calculator.push((num2 - num1) + "");
            } else if ("/".equals(exp)) {
                Integer num1 = Integer.parseInt(calculator.pop());
                Integer num2 = Integer.parseInt(calculator.pop());
                calculator.push((num2 / num1) + "");
            } else {
                throw new UnsupportedOperationException("error operator " + exp);
            }
        }
        // 返回最终结果
        return calculator.isEmpty() ? 0 : Integer.parseInt(calculator.peek());
    }


    public static void main(String[] args) {
        PolandCalculator calculator = new PolandCalculator("1 + ( ( 2 + 3 )* 4) - 5");
        int result = calculator.calc();
        System.out.println(result);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值