算法之逆波兰计算器的分析与实现

关于使用栈实现的普通计算器我之前已经实践过了,但是使用的是普通的中缀算术表达式的方式实现的,感兴趣可以看这篇文章:
https://juejin.im/post/5d72494de51d4561ad65492d
但是一般在计算机的本地存储中,如果是使用中缀表达式的话,对于计算机来说,是很大的计算和存储负担,因此在计算机的设计中,基本是将来人来说简单容易理解的中缀表达式转化为后缀表达式来存储,也叫做逆波兰表达式。使用后缀表达式的方式能够计算出结果的计算器,就是逆波兰计算器。

题目:
输入一个后缀表达式的,使用栈(Stack),计算其结果。能够支持小括号和多位整数(暂不包括小数)

见代码:

/**
 * @author 曾鑫曜(xinyao.zeng @ *******)
 * @version 1.0
 * @description:
 * @since 2019/9/17 16:31
 */
public class PolandNotation {

    //先定义给逆波兰表达式
    //(30+4)×5-6  => 30 4 + 5 × 6 - => 164
    // 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / +
    //测试
    //说明为了方便,逆波兰表达式 的数字和符号使用空格隔开
    public static void main(String[] args) {
    //String suffixExpression = "30 4 + 5 * 6 -";
    // 76
  /*  String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";
    //思路
    //1. 先将 "3 4 + 5 × 6 - " => 放到ArrayList中
    //2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算

        List<String> list = getListString(suffixExpression);


        System.out.println("rpnList=" + list);
        int res = calculate(list);


        System.out.println("计算的结果是=" + res);*/


        String expression = "1+((12+32)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
        System.out.println("中缀表达式对应的List=" + infixExpressionList);
        List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
        System.out.println("后缀表达式对应的List" + suffixExpreesionList); //ArrayList [1,2,3,+,4,*,+,5,–]
        System.out.printf("expression=%d", calculate(suffixExpreesionList)); // ?
    }

    //方法:将 中缀表达式转成对应的List
    //  s="1+((2+3)×4)-5";
    public static List<String> toInfixExpressionList(String s) {

        //定义一个List,存放中缀表达式 对应的内容
        List<String> ls = new ArrayList<String>();
        int i = 0; //这时是一个指针,用于遍历 中缀表达式字符串
        String str; // 对多位数的拼接
        char c; // 每遍历到一个字符,就放入到c
        do {
            //如果c是一个非数字,我需要加入到ls
            if((c=s.charAt(i)) < 48 ||  (c=s.charAt(i)) > 57) {
                ls.add("" + c);
                i++; //i需要后移
            } else { //如果是一个数,需要考虑多位数
                str = ""; //先将str 置成"" '0'[48]->'9'[57]
                while(i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57) {
                    str += c;//拼接
                    i++;
                }
                ls.add(str);
            }
        }while(i < s.length());
        return ls;//返回
    }

    //即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]  =》 ArrayList [1,2,3,+,4,*,+,5,–]
    //方法:将得到的中缀表达式对应的List => 后缀表达式对应的List
    public static List<String> parseSuffixExpreesionList(List<String> ls) {
        // 定义两个栈
        Stack<String> s1 = new Stack<String>(); // 符号栈
        //说明:因为s2 这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出
        //因此比较麻烦,这里我们就不用 Stack<String> 直接使用 List<String> s2
        //Stack<String> s2 = new Stack<String>(); // 储存中间结果的栈s2
        List<String> s2 = new ArrayList<String>(); // 储存中间结果的Lists2

        for(String item: ls){
            //如果是数字直接存入栈s2
            if(item.matches("\\d+")){
                s2.add(item);
            } else if (item.equals("(")){
                s1.push(item);
            } else if(item.equals(")")){
                while(!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();
            } else {
                while(s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                s1.push(item);
            }
        }

        while(s1.size()!=0){
            s2.add(s1.pop());
        }
        return s2;
    }

    /**
     * 将一个逆波兰表达式, 依次将数据和运算符 放入到 ArrayList中
     * @param suffixExpression
     * @return list
     */
    public static List<String> getListString(String suffixExpression) {
        String[] strings = suffixExpression.split(" ");
        ArrayList<String> list = Lists.newArrayList(strings);
        return list;
    }

    //编写一个类 Operation 可以返回一个运算符 对应的优先级
    static class Operation {
        private static int ADD = 1;
        private static int SUB = 1;
        private static int MUL = 2;
        private static int DIV = 2;

        //写一个方法,返回对应的优先级数字
        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:
                    System.out.println("不存在该运算符" + operation);
                    break;
            }
            return result;
        }

    }


    /**
     * 完成对逆波兰表达式的运算
     * 1)从左至右扫描,将3和4压入堆栈;
        2)遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
        3)将5入栈;
        4)接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
        5)将6入栈;
        6)最后是-运算符,计算出35-6的值,即29,由此得出最终结果
     */
    public static int calculate(List<String> list) {
        //创建栈,只需要一个
        Stack<String> stack = new Stack<String>();
        //遍历
        list.stream().forEach(x -> {

            //匹配的是多位数
            if(x.matches("\\d+")){
                stack.push(x);
            } else {
                //pop出两位数
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                switch (x) {
                    case "+":
                        res = num1 + num2;
                        break;
                    case "-":
                        res = num1-num2;
                        break;
                    case "*":
                        res = num1 * num2;
                        break;
                    case "/":
                        res = num1 / num2;
                        break;
                    default:
                        throw new RuntimeException("运算符有误");
                }
                stack.push("" + res);
            }
        });
        int result = Integer.parseInt(stack.pop());
        return result;
    }
}

解题思路如下:


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容