算法第一讲(使用栈实现计算)

算法第一讲(使用栈实现计算器)

从2019年9月开始,会把《数据结构》经典的算法介绍一遍。加油,89lovelc

问题介绍
  • 用户输入一个包含+,-,*,/,正整数,圆括号的合法算数表达式,计算该表达式的结果
问题分析
  • 分析题目
    • 实际生活中,我们使用的都是中缀表达式,意思就是1+(2*3)-2,这种就是中缀表达式,优点就是易于人类阅读,使用括号来表达算法的优先级的顺序,就比如说上面的例子,应该是在1加之前先算括号里面的2 * 3, 之后再算-2得出结果。
    • 使用后缀表达式,我们使用后缀表达式来进行改变上面的例子,就可以变成1 2 3 * + 2- ,意思就是 数字 数字 运算符,相应的也有前缀表达式:运算符 数字 数字。如果用后缀表达式,我们就可以尝试的使用计算机的语言来做一遍:
      • 把 1 压入栈 2压入栈 3压入栈
      • 发现 * ,将2 3弹出,并运算得到6,将6压入栈
      • 发现+ ,将 1 6弹出,并运算得到7 ,将7压入栈
      • 将 2 压入栈,发现 -,将 7 2弹出,并运算的到5,将5压入栈
      • 没有表达式,将5出栈
    • 这就演示了一个例子出来,可以得出我们要得到解有两部分组成
      • 中缀表达式变后缀表达式
      • 后缀表达式进行计算
算法思想
  • 中缀表达式变后缀表达式
    • 1 + 2 * 3
      • 首先我们有一个运算栈的概念,这个栈进行放入运算符,通过进栈出栈达到计算的先后顺序,来看上面的式子。遇到数字放入到exp字符串里面去,看到 + 运算符,因为栈为空,直接压入栈。然后到了如图所示的
      • 后缀表达式第一部分
      • 然后 * 要进来了,发现 * 的优先级要比+ 的优先级要高,根据栈的先进后出,后进先出的规则,将 * 压入栈,表达式没有之后分别弹出运算栈的东西,就变成了 1 2 3 * +,如果是 1 + 2 + 3的时候,当遇到第二个+的时候,发现肯定是按照顺序执行所以应该是将第一个+出栈之后,在压入栈。所以我们得到第一个结论。
      • 有数字的时候直接放入exp字符串中,当运算符的时候,如果运算栈为空就直接压入栈,如果有运算符,查看优先级是否大于自己,如果大于或等于自己就出栈,直到栈顶运算符小于自己的话,再将其入栈。
    • 1 * ( 2 + 4 ) - 3
      • 后缀表达式第二部分
    • 如上图,因为() 是开启一个子表达式,所以当运算符是 ( 的时候,可以直接入栈,如果是 ) 的时候, ) 表达的师一个子表达式的结束,所以在出栈顺序的第一个( 的中间的运算符都要进行出栈操作,并且存入exp字符串,那么得到的结果就是 1 2 4 + * 3 -,得到结论。
    • 如果是(,直接入栈,如果是),在第一次出现( 中间的运算符全部出栈,(的优先级比+,- 还要低。
  • 后缀表达式进行计算
    • 因为变成后缀之后,数字之间没有分隔来进行表示,我们这里统一使用#号进行分隔。
    • (22+33)*(11+80) ==> 22#33#+#11#80#+#*#
    • 使用后缀就好算了,将#分隔之后从左往右取值,如果是数字压入栈,如果是运算符连续弹出栈两次,后面弹出的为第一个数,然后进行计算结果压入栈即可。
代码实现
package com.lovelc;

import java.util.Objects;
import java.util.Stack;

public class OperationStack {


    /**
     * 将中缀变成后缀 其中数字将# 给隔开
     *
     * @param string
     * @return
     */
    public static String infix2suffix(String string) {
        Stack<Character> opr = new Stack<Character>();
        StringBuilder stringBuilder = new StringBuilder("");
        char[] chars = string.toCharArray();
        int size = chars.length;
        int index = 0;
        while (index < size) {


            switch (chars[index]) {
                case '(':
                    opr.push(chars[index]);
                    break;
                case ')':
                    while (opr.peek() != '(') {
                        stringBuilder.append(opr.pop()).append("#");
                    }
                    opr.pop();
                    break;
                case '+':
                case '-':
                    while (!opr.isEmpty() && opr.peek() != '(') {
                        stringBuilder.append(opr.pop()).append("#");
                    }
                    opr.push(chars[index]);
                    break;
                case '*':
                case '/':
                    if (opr.isEmpty()) {
                        opr.push(chars[index]);
                        break;
                    }

                    while (opr.peek() == '*' || opr.peek() == '/') {
                        stringBuilder.append(opr.pop()).append("#");
                    }
                    opr.push(chars[index]);
                    break;
                default:
                    //成为数字的时候
                    int num = chars[index] - '0';
                    while ((index + 1) < size && chars[index + 1] >= '0' && chars[index + 1] <= '9') {
                        num = num * 10 + chars[index + 1] - '0';
                        index++;
                    }
                    stringBuilder.append(num).append("#");

                    break;

            }

            index++;


        }

        while (!opr.isEmpty()) {
            stringBuilder.append(opr.pop()).append("#");
        }

        return stringBuilder.toString();
    }


    /**
     * 将后缀表达式进行计算
     *
     * @param string
     * @return
     */
    public static int calculateSuffix(String string) {
        String[] items = string.split("#");
        Stack<Integer> stack = new Stack<Integer>();

        for (String item : items) {
            if ("+-*/".contains(item)) {
                int num_2 = stack.pop();
                int num_1 = stack.pop();

                stack.push(calculateOpr(num_1, item, num_2));
                continue;
            }
            stack.push(Integer.parseInt(item));
        }


        return stack.pop();
    }

    private static Integer calculateOpr(int num_1, String item, int num_2) {
        if (Objects.equals(item, "+")) {
            return num_1 + num_2;
        }

        if (Objects.equals(item, "-")) {
            return num_1 - num_2;
        }

        if (Objects.equals(item, "*")) {
            return num_1 * num_2;
        }

        if (Objects.equals(item, "/")) {
            return num_1 / num_2;
        }

        return 0;
    }


    public static Integer calculate(String string) {
        System.out.println("得到的式子为" + string);
        String suffix = infix2suffix(string);
        System.out.println("得到后缀式子为" + suffix);
        int result = calculateSuffix(suffix);
        System.out.println("最后结果为" + result);

        return result;
    }


    public static void main(String[] args) {
        calculate("1+2+3");
        System.out.println();


        calculate("2*3-1");
        System.out.println();


        calculate("(2+10)/(3-1)+4*1");
        System.out.println();

    }

}

运行结果

运算栈的结果

总结
  • 这个算法是充分的利用栈的特点:先进后出,后进先出。将其转化为后缀表达式之后消除括号,然后进行计算,之后我会带来一些其他使用栈的内容。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值