[044] 使用List和Stack实现综合计算器(后缀--逆波兰表达式)

1、后缀表达式

中缀表达式如(3+4)*5-6是我们人类在书写数学表达式时最长使用的一种书写方式,但对于计算机来说,后缀表达式(又称为逆波兰表达式)更利于计算。

  • 中缀表达式: (3+4)*5-6 -> 后缀表达式(逆波兰表达式):3 4 + 5 * 6 -
  • 中缀表达式:4*5-8+60+8/2 -> 后缀表达式(逆波兰表达式):4 5 * 8 - 60 + 8 2 / +

后缀计算器的设计步骤如下:

  • 将中缀表达式String转为中缀表达式List;
  • 将中缀表达式List转为后缀表达式List;
  • 将后缀表达式存在栈Stack中,运用栈的特性完成计算。

2、Java代码

package DataStructures.Stack;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

/**
 * @author yhx
 * @date 2020/09/21
 */
public class PolandNotation {
    /**
     * ASCII码表中数字字符0和9对应的编号
     */
    private static final int ASCII_0 = 48;
    private static final int ASCII_9 = 57;
    private static final String BRACKET_LEFT = "(";
    private static final String BRACKET_RIGHT = ")";

    public static void main(String[] args) {
        //定义一个中缀表达式:
        String expression = "(4+5)*(8+60)+8/2";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("中缀表达式:" + infixExpressionList);
        List<String> suffixExpression = parseSuffixExpressionList(infixExpressionList);
        System.out.println("后缀表达式:" + suffixExpression);

        System.out.printf("表达式计算结果 = %d",calculate(suffixExpression));
    }

    /**
     * 将中缀表达式 list 转为后缀表达式 list
     *
     * @param ls 中缀表达式list
     * @return 返回后缀表达式 list
     */
    public static List<String> parseSuffixExpressionList(List<String> ls) {
        //定义一个栈s1用于存储操作符 和 一个s2用于存储中间结果
        Stack<String> s1 = new Stack<>();
        List<String> s2 = new ArrayList<>();
        //遍历ls
        for (String item : ls) {
            //如果是数,就入list
            if (item.matches("\\d+")) {
                s2.add(item);
            } else if (item.equals(BRACKET_LEFT)) {
                //如果是左括号就将括号入栈s1
                s1.push(item);
            } else if (item.equals(BRACKET_RIGHT)) {
                //如果是右括号")",则依次弹出s1栈顶的运算符,并转存到s2中,弹到遇到左括号"("为止,此时就将一对括号丢弃
                //peek用于查看栈顶的元素值
                while (!s1.peek().equals(BRACKET_LEFT)) {
                    s2.add(s1.pop());
                }
                //将栈顶的左括号删除
                s1.pop();
            } else {
                //当item的优先级小于等于s1栈顶运算符的优先级,则将s1栈顶的运算符弹出并加入到s2中,重复该操作
                while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
                    s2.add(s1.pop());
                }
                //当s1中优先级比item高的运算符都被转移到s2之后,将item压入s1
                s1.push(item);
            }
        }
        //将s1中剩余的运算符以及弹出并加入到s2中
        while (s1.size() != 0) {
            s2.add(s1.pop());
        }
        //最终存放在s2中数据,就是逆波兰表达式对应的list
        return s2;
    }

    /**
     * 将中缀表达式转为对应的List
     *
     * @param s 中缀表达式String
     * @return 返回该中缀表达式List类型
     */
    public static List<String> toInfixExpressionList(String s) {
        //存放中缀表达式List
        List<String> ls = new ArrayList<>();
        //指针,遍历中缀表达式字符串
        int i = 0;
        //对多位数拼接
        StringBuilder str;

        do {
            //如果c是一个非数字,就将其加入到ls中
            if (s.charAt(i) < ASCII_0 || s.charAt(i) > ASCII_9) {
                ls.add(s.charAt(i) + "");
                i++;
            }
            //如果是一个数('0'[48] ~ '0'[57]),则需要考虑多位
            else {
                //先将str置为空"",
                str = new StringBuilder();
                while (i < s.length() && s.charAt(i) >= ASCII_0 && s.charAt(i) <= ASCII_9) {
                    //拼接,str.append(c)等价于str += c
                    str.append(s.charAt(i));
                    i++;
                }
                ls.add(str.toString());
            }
        } while (i < s.length());
        return ls;
    }

    /**
     * 将str字符串转为List表的形式,便于遍历操作
     *
     * @param suffixExpression 后缀表达式的str字符串
     * @return 返回后缀表达式List
     */
    public static List<String> getListString(String suffixExpression) {
        //以空格为标志,将字符串suffixExpression分割为字符串数组
        String[] split = suffixExpression.split(" ");
        List<String> list = new ArrayList<>();
        //将字符串数组中的元素全部添加到字符串列表中
        Collections.addAll(list, split);
        return list;
    }

    /**
     * 将后缀表达式List存到栈中,并完成计算
     *
     * @param ls 后缀表达式List
     * @return 返回计算结果
     */
    public static int calculate(List<String> ls) {
        //创建一个栈即可
        Stack<String> stack = new Stack<>();
        //遍历ls
        for (String item : ls) {
            //如果item是数,则入栈。matches是正则表达式的匹配函数,"\\d+"表示匹配数字(+说明可匹配多位数)
            if (item.matches("\\d+")) {
                stack.push(item);
            } else {
                //如果匹配到操作符,就从栈中取出两个数计算,再将结果放回栈中
                //因为栈中的元素类型是String,所以需要用Integer.parseInt转为整数型
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res;
                switch (item) {
                    case "+":
                        res = num1 + num2;
                        break;
                    case "-":
                        res = num1 - num2;
                        break;
                    case "*":
                        res = num1 * num2;
                        break;
                    case "/":
                        res = num1 / num2;
                        break;
                    default:
                        throw new RuntimeException("运算符有误");
                }
                //将运算结果重新放回栈中。但res是整型,栈是String类型,此处用个小技巧可将res强转为String:res + ""
                stack.push(res + "");
            }
        }
        //最后留在栈中的就是运算结果
        return Integer.parseInt(stack.pop());
    }

}

/**
 * 用于比较运算符优先级的类
 */
class Operation {
    private static final int ADD = 1;
    private static final int SUB = 1;
    private static final int MUL = 2;
    private static final int DIV = 2;

    /**
     * @param operation 操作符
     * @return 返回对应的优先级
     */
    public static int getValue(String operation) {
        int result = 0;
        switch (operation) {
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                break;
        }
        return result;
    }

}

3、运行结果

中缀表达式:[(, 4, +, 5, ), *, (, 8, +, 60, ), +, 8, /, 2]
后缀表达式:[4, 5, +, 8, 60, +, *, 8, 2, /, +]
表达式计算结果 = 616

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值