用 Stack 计算四则运算表达式的值

大家都知道四则运算表达式是有优先条件的 。比如先乘除再加减。而且如果有括号的话还要先计算括号里面。有没有想过现在的高级计算器是如何实现这一功能的呢?

这里面的困难就在于乘除有时候在后面却要先计算,在加上有括号的情况就更加复杂了。

  • 后缀表达式

类似 9 + (3 - 1) * 3 + 10 / 2 是我们常见的表达式,叫『中缀表达式』。后缀表达式不同与中缀表达式,之所以叫“后缀表达式”是因为所有的运算符都在数字后面出现。所以9 + (3 - 1) * 3 + 10 / 2的后缀表达式为:9 3 1 - 3 * + 10 2 / +
显然,是没有括号的。

  • 后缀表达式计算结果

后缀表达式为:9 3 1 - 3 * + 10 2 / +

规则:从左到右遍历遍历表达式的每一个数字和符号,遇到数字就进栈 ,遇到符号就栈顶的两个数字出栈,然后进行运算,然后将运算结果进栈 。一直到最后,栈里面的值即为最终结果。

  1. 初始化空栈, 9 3 1 依次进栈。栈内从下到上依次是:9,3,1
  2. 然后 - 号进栈,3 - 1 = 2, 2 进栈。栈内从下到上依次是:9,2
  3. 接着 3 进栈。栈内从下到上依次是:9,2,3
  4. * 号进栈,2 * 3 = 6,6 进栈。栈内从下到上依次是:9,6
  5. + 号进栈,9 + 6 = 15, 15 进栈。栈内从下到上依次是: 15
  6. 10, 2 依次进栈。栈内从下到上依次是: 15,10,2
  7. / 号进栈,10 / 2 = 5, 5 进栈。栈内从下到上依次是: 15,5
  8. + 号进栈,15 + 5 = 20。20 进栈。

至此结束,表达式最终结果:20

看来计算机果然适合计算后缀表达式求值,那么现在的问题就是如何把我们常见的中缀表达式转换为后缀表达式呢?


  • 中缀表达式转后缀表达式

规则:从左到右遍历表达式的每个数字和符号

  1. 遇到数字直接输出,成为后缀表达式的一部分。
  2. 栈为空时,遇到运算符,直接入栈
  3. 当前符号是 (, 则直接进栈
  4. 当前符号是 ), 则把栈中的符号依次出栈,直到遇到 )为止。) 不输出。
  5. 若是 + - * / 运算符号,弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈;直到遇到栈顶符号为 ) 或栈空 [+ - 为一级,* / 为 二级]

中缀表达式 9 + (3 - 1) * 3 + 10 / 2 转后缀表达式为: 9 3 1 - 3 * + 10 2 / +

  1. 9 直接输出。结果集:9;
  2. + 入栈。结果集:9;栈内从下到上依次是:+
  3. ( 直接入栈。结果集:9;栈内从下到上依次是:+,(
  4. 3 直接输出。结果集:9,3;栈内从下到上依次是:+,(
  5. - 入栈,优先级低于栈顶符号 (,直接入栈。结果集:9,3;栈内从下到上依次是:+,(,-
  6. 1 直接输出。结果集:9,3,1;栈内从下到上依次是:+,(,-
  7. 下一个符号是 ),因此循环出栈,直到遇到 (.结果集:9,3,1,-;栈内从下到上依次是:+
  8. * 入栈。因为栈顶是 +,优先级低于 *, 所以 * 直接入栈。结果集:9,3,1,-;栈内从下到上依次是:+,*
  9. 3 直接输出。结果集:9,3,1,-,3;栈内从下到上依次是:+,*
  10. + 入栈,栈顶 * 优先级高于 +,固 * 出栈并输出。下一个是 +, 优先级等于 +。出栈,+ 入栈。结果集:9,3,1,-,3,*,+;栈内从下到上依次是:+
    此时栈底的+是3+10的+,表达式里面的+是第一个+
  11. 10 直接输出。结果集:9,3,1,-,3,*,+,10;栈内从下到上依次是:+
  12. / 入栈,优先级高于栈顶符号 +。直接入栈。结果集:9,3,1,-,3,*,+,10;栈内从下到上依次是:+,/
  13. 2 直接输出。结果集:9,3,1,-,3,*,+,10,2;栈内从下到上依次是:+,/
  14. 循环出栈。结果集:9,3,1,-,3,*,+,10,2,/,+
代码如下:
/**
     * 根据后缀表达式计算结果集
     * @param result
     * @return
     */
    private double getResult(List<String> result) {
        if (null == result || result.size() == 0) {
            throw new RuntimeException("表达式不合法!");
        }
        Stack<String> stack = new Stack<>();
        Set<String> operation = new HashSet<>(Arrays.asList("+", "-", "*", "/"));
        double d = 0.0d;
        for (String str : result) {
            if (!operation.contains(str)) {
                stack.push(str);
            } else {
                double up = Double.parseDouble(stack.pop());
                double down = Double.parseDouble(stack.pop());
                switch (str) {
                    case "+":
                        d = down + up;
                        break;
                    case "-":
                        d = down - up;
                        break;
                    case "*":
                        d = down * up;
                        break;
                    case "/":
                        d = down / up;
                        break;
                    default:
                        break;
                }
                stack.push(String.valueOf(d));
            }
        }
        return Double.parseDouble(stack.pop());
    }


/**
     * 将中缀表达式转换为后缀表达式
     *
     * @param operationExpression
     * @return
     */
    private List<String> operationExpressionToRPN(String operationExpression) {
        if (null == operationExpression || "".equals(operationExpression)) {
            throw new RuntimeException("表达式不合法!");
        }
        List<String> result = new LinkedList<>();

        char[] chars = operationExpression.toCharArray();

        Set<String> operation = new HashSet<>(Arrays.asList("+", "-", "*", "/"));
        Set<String> numbers = new HashSet<>(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));


        Stack<String> stack = new Stack<>();
        StringBuffer sb = new StringBuffer();
        String lastChar = "";
        for (char c : chars) {

            String currentChar = String.valueOf(c);

            // 上一个字符 和 当前字符 都是数字的话
            if (numbers.contains(lastChar) && numbers.contains(currentChar)) {
                String lastestChar = result.get(result.size() - 1);
                result.remove(result.size() - 1);
                result.add(lastestChar + currentChar);
                lastChar = currentChar;
                continue;
            }
            if (numbers.contains(currentChar)) {
                sb.append(currentChar);
            } else {
                /**
                 *
                 * 1:当前符号是 (, 则直接进栈
                 * 2:当前符号是 + - * /, 弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈
                 * 3:当前符号是 ), 则把栈中的符号依次出栈,直到遇到 )为止。
                 */

                if ("(".equals(currentChar)) {
                    stack.push(currentChar);
                } else if (operation.contains(currentChar)) {
                    /*while (true) {
                        // 栈空、栈顶符号为"("、当前符号优先级 > 栈顶符号优先级。当前符号入栈
                        if (stack.isEmpty() || getOperationLevel(stack.peek()) < getOperationLevel(currentChar) || "(".equals(stack.peek())) {
                            stack.push(currentChar);
                            break;
                        } else {
                            result.add(stack.pop());
                        }
                    }*/
                    while(!stack.isEmpty() && (getOperationLevel(stack.peek()) >= getOperationLevel(currentChar)) && !"(".equals(stack.peek())){
                        result.add(stack.pop());
                    }
                    stack.push(currentChar);
                } else if (")".equals(currentChar)) {
                    String str;
                    while (!stack.isEmpty() && !"(".equals(str = stack.pop())) {
                        result.add(str);
                    }
                }
            }

            if (!"".equals(sb.toString())) {
                result.add(sb.toString());
                sb.delete(0, sb.length());
            }
            lastChar = currentChar;
        }
        // 最后清空栈
        while (!stack.isEmpty()) {
            result.add(stack.pop());
        }
        return result;
    }
    
    private int getOperationLevel(String operation) {
        int result;
        switch (operation) {
            case "+":
                result = 1;
                break;
            case "-":
                result = 1;
                break;
            case "*":
                result = 2;
                break;
            case "/":
                result = 2;
                break;
            default:
                result = 0;
                break;
        }
        return result;
    }
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容