java栈实现简单的计算器

一、代码

直接上代码,整个代码分为两个类calc.StackCalculator.java,calc.Calculator.java
1、StackCalculator.java

/**
 * 用栈实现表达式的运算v1.0
 * 支持运算:+、-、*、/、%、^、!、()
 * 输入的表达式需以"#"结束
 */
package calc;

import java.util.Stack;

public class StackCalculator {
    private Stack optr = new Stack();// 操作符栈
    private Stack opnd = new Stack();// 操作数栈
    private final Character END_Character = '#';// 输入表达式的结束字符

    public final int ADD = 0, SUB = 1, MUL = 2, DIV = 3, MOD = 4, POW = 5, FAC = 6, L_P = 7, R_P = 8, EOF = 9;
    public final char[][] PRI = {//运算符优先等级  [栈顶]'levle'[当前]
            //         |----------------当前运算符----------------|
            //           +   -   *   /   %   ^   !   (   )   #(结束字符)
            /*    + */ {'>','>','<','<','<','<','<','<','>','>'},
            /*    - */ {'>','>','<','<','<','<','<','<','>','>'},
            /* 栈     * */ {'>','>','>','>','>','<','<','<','>','>'},
            /* 顶     / */ {'>','>','>','>','>','<','<','<','>','>'},
            /* 运     % */ {'>','>','>','>','>','<','<','<','>','>'},
            /* 算     ^ */ {'>','>','>','>','>','>','<','<','>','>'},
            /* 符     ! */ {'>','>','>','>','>','>','>',' ','>','>'},
            /*    ( */ {'<','<','<','<','<','<','<','<','=',' '},
            /*    ) */ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
            /*    # */ {'<','<','<','<','<','<','<','<',' ','='}
    };

    /**
     * 中缀表达式计算,程序核心部分
     * @param expression
     *            输入的有效中缀表达式
     * @param RPN
     *            输出的逆波兰表达式(Reverse Polish Notation)
     * @return
     */
    public float calcExpression(String expression, StringBuffer RPN) throws Exception {
        // 初始时操作符栈压入开始标识符,与结束字符对应
        optr.push(END_Character);

        // 剔除表达式中的空格
        expression = removeSpace(expression);

        int i = 0;// 表示读取表示式的位置
        while (!optr.empty()) {
            char currenSymbol = expression.charAt(i);// 当前扫描到的表达式符号
            if (Character.isDigit(currenSymbol)) {
                i = readNumber(expression, i, opnd);// 读入(可能多位的)操作数
                RPN.append(opnd.peek() + " ");// 计入逆波兰表达式
            } else {
                switch (orderBetween(optr.peek(), currenSymbol)) {
                case '>':// 栈顶运算符的优先级大于当前运算符,则1.取出操作符栈顶运算符,2.取出数据栈中的一个或多个数据(取决于运算符的类型),3.运算并把结果压入数据栈中
                    char op = optr.pop();// 取出栈顶操作符
                    RPN.append(op + " ");// 当操作符可以计算时计入逆波兰表达式,与逆波兰表达式的计算过程恰好吻合

                    Calculator ca = new Calculator();// 基本计算操作对象

                    if ('!' == op) {// 一元运算符的计算
                        float number = opnd.pop();// 取出操作数栈顶数值
                        System.out.println("计算过程:" + "[" + number + "]" + "" + op + "=" + "[" +ca.calcu(number, op) +"]" );
                        opnd.push(ca.calcu(number, op));// 计算并将结果入栈
                    } else {// 二元运算符的计算
                        float number2 = opnd.pop();// 取出操作数栈顶数值
                        float number1 = opnd.pop();// 取出操作数栈顶数值
                        System.out.println("计算过程:" + "[" + number1 + "]" + "" + op + "" + "[" + number2 + "]" + "=" + "[" +ca.calcu(number1, op, number2) + "]");
                        opnd.push(ca.calcu(number1, op, number2));// 计算并将结果入栈
                    }
                    break;
                case '<':// 栈顶运算符的优先级小于当前运算符,计算推迟,当前运算符入栈
                    optr.push(currenSymbol);
                    i++;
                    break;
                case '=':// 栈顶运算符的优先级等于当前运算符,脱括号并接收下一个字符
                    optr.pop();
                    i++;
                    break;
                case ' ':
                    throw new Exception("ERROR");
                }
            }
        }

        return opnd.pop();// 操作数栈的最后一个元素即为需要的结果
    }

    /**
     * 只关注于求中缀表达式的接口
     * @param expression
     * @return
     * @throws Exception
     */
    public float calcExpression(String expression) throws Exception {
        return calcExpression(expression, new StringBuffer());
    }

     /**
      * 对逆波兰表达式进行计算
      * @param rpn
      * @return
      * @throws Exception
      */
     public float calcExpressionRpn(String rpn) throws Exception{
         String[] chs = rpn.split(" ");
         int i = 0;
         int chLength = chs.length;
         while(i < chLength) {
             Calculator ca = new Calculator();// 基本计算操作对象
             if( convertStrToDigit(chs[i]) != null ) {// 操作数直接入栈
                 opnd.push(convertStrToDigit(chs[i]));
             } else {
                 char op = chs[i].charAt(0);
                 if("!".equals( chs[i] )) {
                     float number = opnd.pop();
                     opnd.push(ca.calcu(number, op));
                     System.out.println("计算过程:" + "[" + number + "]" + "" + op + "=" + "[" +ca.calcu(number, op) +"]" );

                 } else {
                     float number2 = opnd.pop();// 取出操作数栈顶数值
                     float number1 = opnd.pop();// 取出操作数栈顶数值
                     System.out.println("计算过程:" + "[" + number1 + "]" + "" + op + "" + "[" + number2 + "]" + "=" + "[" +ca.calcu(number1, op, number2) + "]");
                     opnd.push(ca.calcu(number1, op, number2));// 计算并将结果入栈
                 }
             }
             i++;
         }
         return opnd.pop();
     }

     /**
      * 将字符串转化为浮点数
      * @param str
      * @return
      */
     private Float convertStrToDigit(String str) {
         try{
             float num = Float.valueOf(str);
             return num;
         } catch(Exception e){
             return null;
         }
     }

    /**
     *  将char型的数字字符转为浮点型数据
     * @param ch
     * @return
     */
    private float CharToFloat(char ch) {
        return Float.valueOf("" + ch);
    }

    /**
     * 将起始为i的子串解析为数值,并存入操作数栈中
     * @param expression 表达式
     * @param i 开始解析的位置
     * @param stk 将解析完毕的数值存入此栈中
     * @return 该数值解析完毕后,返回表达式需要解析的下一个位置
     * @throws Exception 解析错误
     */
    private int readNumber(String expression, int i, Stack stk) throws Exception {
        stk.push(CharToFloat(expression.charAt(i++)));// 当前数位对应的数值进栈
        char op = expression.charAt(i); // 读取下一个字符
        while (Character.isDigit(op)) {// 只要后续还有紧邻的数字
            stk.push(stk.pop() * 10 + CharToFloat(op));// 弹出原操作数并追加新数位后,新数值重新入栈
            op = expression.charAt(++i);// 下一个字符
        }
        if ('.' != op)
            return i;// 如果最后一个数字后面不是小数点,说明解析完成,则返回当前位置
        op = expression.charAt(++i);
        float fraction = 1;
        while (Character.isDigit(op)) {
            stk.push(stk.pop() + CharToFloat(op) * (fraction /= 10));
            op = expression.charAt(++i);
        }
        if ('.' == op)
            throw new Exception("ERROR");// 如果还有小数点则错误
        return i;// 返回当前解析字符的位置
    }

    /**
     * 根据运算符获取在优先级表中的秩(RANK)
     * @param op
     * @return
     * @throws Exception
     */
    private int getOperRank(char op) throws Exception {
        switch (op) {
        case '+':
            return ADD;
        case '-':
            return SUB;
        case '*':
            return MUL;
        case '/':
            return DIV;
        case '%':
            return MOD;
        case '^':
            return POW;
        case '!':
            return FAC;
        case '(':
            return L_P;
        case ')':
            return R_P;
        case '#':
            return EOF;
        default:
            throw new Exception("ERROR");
        }
    }

    /**
     * 比较栈顶和当前字符的优先级
     * @param peekOptr 栈顶运算符
     * @param currenOptr 当前扫描到的运算符
     * @return 优先级表中的数据项
     * @throws Exception
     */
    private Character orderBetween(Character peekOptr, char currenOptr) throws Exception {
        return PRI[getOperRank(peekOptr)][getOperRank(currenOptr)];
    }

    /**
     * 剔除字符串之间的空格
     * @param str
     * @return
     */
    public String removeSpace(String str) {
        char[] chs = str.toCharArray();
        StringBuffer newStr = new StringBuffer();
        int i = 0;
        while (i < str.length()) {
            if (' ' != chs[i]) {
                newStr.append(chs[i]);
            }
            i++;
        }
        return newStr.toString();
    }

    // test
    public static void main(String[] args) {
        StackCalculator sc = new StackCalculator();
        String s = "(1 + 2^3!-4)*(5!-(6-( 7-(89-0!))))#";// 2013
        StringBuffer rpn = new StringBuffer();
        try {
            System.out.println("结果:" + sc.calcExpression(s, rpn));
            System.out.println("逆波兰表达式:" + rpn);

            System.out.println("\n计算逆波兰表达式:");
            sc.calcExpressionRpn(rpn.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2、Calculator.java

package calc;

public class Calculator {
    /**
     * 一元运算符
     * 
     * @param a
     *            操作数
     * @param op
     *            操作符
     * @return 结果浮点型
     */
    public float calcu(float n, char op) throws Exception {
        return fact(n);// 这里只有阶乘一个一元运算符
    }

    /**
     * 二元运算符
     * 
     * @param a
     *            操作数1
     * @param op
     *            操作符
     * @param b
     *            操作数2
     * @return 结果浮点型
     * @throws Exception
     */
    public float calcu(float a, char op, float b) throws Exception {
        switch (op) {
        case '+':
            return a + b;
        case '-':
            return a - b;
        case '*':
            return a * b;
        case '/':
            return div(a, b);
        case '%':
            return mod(a, b);
        case '^':
            return (float) Math.pow(a, b);
        default:
            throw new Exception("ERROR");
        }
    }

    private float div(float a, float b) throws Exception {
        if (b == 0)
            throw new Exception("除数不能为0!");
        return a / b;
    }

    // 取余
    private float mod(float a, float b) throws Exception {
        if (b == 0)
            throw new Exception("除数不能为0!");
        return a % b;
    }

    // 阶乘 n!(n<=20)
    private float fact(float n) throws Exception {
        if (n < 0)
            throw new Exception("阶乘运算数不能为负数!");
        if (n > 34)
            throw new Exception("阶乘数不能超过34,否则越界!");// 可以考虑改进以使计算更大数值的阶乘

        if (n == 0)
            return 1;// 0!=1
        int num = (int) n;
        if (num == n) {// n为整数
            float result = 1;
            while (num > 0) {
                result *= num--;
            }
            return result;
        } else
            throw new Exception("阶乘运算数必须为整数!");
    }

}

二、视频资源

如果想要弄明白关于表达式求值的实现细节和逻辑,建议参考邓俊辉的数据结构MOOC课程(b站上有),下面给出专门讲解表达式求值的视频的百度云链接:https://pan.baidu.com/s/1vlkCpHTVjvzSalgrewgYoA 密码:ixo6

你可能感兴趣的:(数据结构)