使用栈模拟简单计算器详解

使用栈模拟简单计算器详解

1.思路分析

  1. 基于栈先进后出的特性,用来模拟一个简单计算器,进行多位数表达式加减乘除的运算
  2. 定义两个栈,一个栈用来存储数字,一个栈用来存储运算符
  3. 总体思路:将优先级高的运算符存储在符号栈栈顶,优先级低的运算符存储在符号栈的栈底
  4. 如果表达式中的字符是数字,则直接入数栈
  5. 如果是运算符,需要考虑运算符优先级的问题
  6. 考虑如何实现将高优先级的运算符存到栈顶???
  7. 每次在运算符入栈的时候,如果发现符号栈空,则直接入栈,如果发现符号栈不为空,则判断当前运算符和符号栈栈顶的运算符的优先级,如果要入栈的运算符优先级低于符号栈栈顶运算符优先级,则将栈顶的运算符弹出栈直接进行运算,即从数字栈弹出两个数字,从符号栈弹出运算符进行运算,将运算的结果即数字再入数栈,然后将要入栈的符号再入符号栈,这样即可保证栈顶的同优先级的运算符的优先级总是高于栈底运算符优先级
  8. 在表达式字符串中的所有字符全部处理完毕全部入栈后,需要依次出栈数字和运算符进行计算,每次将计算的结果再入数栈,当全部数字运算结束后,数字栈栈底保留了最终的运算结果
  9. 运算结果出栈即可
  10. 详解见源码

2.源码及分析

栈类
//定义一个类表示栈,扩展栈的方法进行表达式的运算
class ArrayStack2 {
    //top表示栈顶的位置,默认为-1,表示栈空
    private int top = -1;
    //maxSize表示栈能存储元素的最大值
    private int maxSize;
    //stack表示用数组模拟栈
    private int[] stack;

    //构造器
    public ArrayStack2(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }

    //判断栈是否满
    public boolean isFull() {
        return top == maxSize - 1;
    }

    //判断栈是否空
    public boolean isEmpty() {
        return top == -1;
    }

    //向栈中添加元素的方法
    public void push(int value) {
        //判断栈是否满
        if (isFull()) {
            System.out.println("栈满,不能添加数据~~");
            return;
        }
        //如果栈不是满的,则栈顶++,将要添加的元素放到栈顶
        top++;
        stack[top] = value;
    }

    //从栈中取出元素的方法
    public int pop() {
        //判断栈是否为空
        if (isEmpty()) {
            throw new RuntimeException("栈是空的~~");
        }
        //如果栈不是空的,则取出栈顶的元素
        int value = stack[top];
        top--;
        return value;
    }

    //查看栈中元素的方法,遍历栈
    public void list() {
        //判断栈是否为空
        if (isEmpty()) {
            System.out.println("栈是空的~~");
            return;
        }
        //如果栈不是空的,则遍历栈
        //注意,遍历栈是从栈顶开始取数据
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

    //编写一个方法返回栈顶的元素,但不是出栈
    public int peek() {
        return stack[top];
    }

    //向栈中添加方法,因为在进行数据的运算时需要大量的方法

    //确定运算符的优先级,运算符的优先级由程序员自己确定,假定运算符越高,数字越大
    public int priority(int operation) {
        if (operation == '*' || operation == '/') {
            return 1;
        } else if (operation == '+' || operation == '-') {
            return 0;
        } else {
            //如果输入的运算符有误
            return -1;
        }
    }

    //判断是否是一个运算符
    public boolean isOperation(char val) {
        return val == '*' || val == '/' || val == '+' || val == '-';
    }

    //计算方法
    public int calculate(int num1, int num2, char operation) {
        //定义变量保存运算返回的结果
        int res = 0;
        switch (operation) {
            case '+':
                res = num1 + num2;
                break;
            //减法注意运算顺序
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                //除法也要注意运算顺序
                res = num2 / num1;
                break;
        }
        return res;
    }


}
模拟计算器的类
public class Calculator {

    public static void main(String[] args) {

        //要计算的表达式
        String expression = "20*40+90000000+2*5*90-30/10";
        //定义一个数字栈用于保存数据,再定义一个符号栈用于保存符号
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 opStack = new ArrayStack2(10);
        //定义一系列变量
        //保存从栈中取出的两个数字
        int num1 = 0;
        int num2 = 0;
        //保存取出的操作符
        char op = 0;
        //保存计算的结果
        int res = 0;
        //保存移动的指针
        int index = 0;
        //保存每次取出的字符
        char ch = 0;

        //循环取字符串的每一个字符并做出相应的操作
        while (true) {
            //先取出字符串的一个字符
            ch = expression.substring(index, index + 1).charAt(0);
            //然后判断这个字符是数字还是符号
            //如果是一个符号
            if (numStack.isOperation(ch)) {
                //判断符号栈满还是空
                //如果符号栈是空的,则将这个运算符直接入栈
                if (opStack.isEmpty()) {
                    opStack.push(ch);
                    //如果符号栈中有运算符,比较刚拿到的运算符和栈顶的运算符的优先级
                } else {
                    //如果优先级低于栈顶符号的优先级,则将栈顶运算符拿出来,
                    // 再从数据栈中拿出两个数据进行运算
                    if (opStack.priority(ch) <= opStack.priority(opStack.peek())) {
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        op = (char) opStack.pop();
                        //进行计算
                        res = numStack.calculate(num1, num2, op);
                        //运算结果入数字栈
                        numStack.push(res);
                        //当前运算符入符号栈
                        opStack.push(ch);
                    } else {
                        //如果拿到的运算符优先级和栈顶的运算符相等或者大于栈顶的运算符
                        //直接入栈
                        opStack.push(ch);
                    }
                }
                //如果是一个数字
            } else {
                //考虑数字是多位数的情况
                //因为 字符 '1' 和数字 1 之间差 48
                //定义n1保存拿到的第一个数字
                int n1 = ch - 48;

                //判断判断数字的位数,如果是多位数,进行数字的拼接处理
                while (true){
                    //定义辅助指针判断数字后边的一位是不是还是数字
                    //因为index的值不能轻易移动,就是后一位是数字则移动,如果不是数字,则不能移动
                    int pointer = index;
                    pointer++;
                    //判断是不是数字,如果是符号,直接返回
                    if (pointer == expression.length() || numStack.isOperation(expression.charAt(pointer))){
                        break;
                    }
                    //如果是数字,index指针移动一位
                    index = pointer;
                    //定义ch2保存读到的下一个低位数字
                    char ch2 = expression.charAt(index);
                    //进行字符和数字的运算处理
                    int n2 = ch2 - 48;

                    //将拿到的两个数字进行处理,将处理后的数字继续保存在n1中
                    n1 = n1 * 10 + n2;
                    //然后循环处理,直到发现下一个是字符
                }
                //循环结束后数字位已经处理完毕,入栈
                numStack.push(n1);
                //numStack.push(ch - 48);
            }
            index++;
            //判断循环结束的条件,即表达式的所有字符全部遍历结束
            if (index == expression.length()) {
                break;
            }
        }
        //循环结束后符号栈中存储的运算符优先级相同,
        //再循环计算这些数字
        while (true) {
            //判断循环结束的条件
            //如果符号栈为空,说明所有的计算已经结束
            if (opStack.isEmpty()) {
                break;
            }
            //循环计算
            num1 = numStack.pop();
            num2 = numStack.pop();
            op = (char) opStack.pop();
            //进行计算
            res = numStack.calculate(num1, num2, op);
            //运算结果入数字栈
            numStack.push(res);
        }

        System.out.println(expression + " = " + numStack.pop());

    }
}

posted @ 2021-05-09 11:36  mx_info  阅读(368)  评论(0)    收藏  举报