栈趣用---中缀表达式实现简易计算器

中缀表达式:我们日常生活中用的表达式就是中缀表达式,比如4+5*6-7,它的特点就是人容易理解和计算,但是计算机不买账,对于中缀表达式,计算机实现起来复杂度很高,这里模拟了计算机计算中缀表达式过程,实现了一个简易计算器。

几个小概念:
前缀表达式:前缀表达式也就是波兰表达式,由相应的语法树前序遍历得到的结果。
中缀表达式:我们最熟悉的一种表达式,先括号,然后乘除,最后加减。它是由相应语法树中序遍历得到结果。
后缀表达式:后缀表达式就是逆波兰表达式,所有操作符都置于操作数的后面,由相应语法树后序遍历得到。
对于中缀表达式,实现计算需要借助两个栈,而对于前缀和后缀表达式,则只需要一个栈来实现,但是前提是必须将中缀表达式转为前缀或者后缀表达式。

1. 栈结构【数组模拟栈】

package datastructure;

/**
 * @author calarqiang
 * @create 2022-04-24-10:12
 * 使用数组模拟栈,然后实现最简单(十位数)的中缀表达式计算
 */
public class SimpleCalc {
    private int MaxSize;
    private int[] stack;
    private int top = -1; //定义栈顶指针,并初始化为-1

    //    初始化栈结构
    public SimpleCalc(int maxSize) {
        this.MaxSize = maxSize;
        this.stack = new int[this.MaxSize];
    }

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

    //    判断栈满
    public boolean isFull() {
        return this.top == this.MaxSize - 1;
    }

    //    入栈操作
    public void push(int num) {
//        先判栈是否满?
        if (isFull()) {
            throw new RuntimeException("栈已满!");
        }
        this.stack[++top] = num;
    }

    //    出栈操作
    public int pop() {
//        判断栈空
        if (isEmpty()) {
            throw new RuntimeException("栈空!");
        }
        int elem = this.stack[top--];
        return elem;
    }

    //    查看栈顶元素
    public int peek() {
        return this.stack[this.top];
    }

    //    遍历栈
    public void print() {
//        判断栈空
        if (isEmpty()) {
            throw new RuntimeException("栈空,没有元素");
        }
        for (int i = this.top; i > -1; i--) {
            System.out.print("stack[" + i + "]=" + this.stack[i] + "\t");
        }
    }

    //   判断运算符优先级,用数字表示
    public static int priority(int oper) {
        if (oper == '*' || oper == '/' || oper == '%') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 0;
        } else {
            return -1;
        }
    }

    //  计算表达式的值
    public static int calc(int num1, int num2, char oper) {
        int res = 0;
        switch (oper) {
            case '+':
                res = num2 + num1;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num2 * num1;
                break;
            case '/':
                res = num2 / num1;
                break;
            case '%':
                res = num2 % num1;
                break;
        }
        return res;
    }

}

2. 中缀表达式计算器实现【借助两个栈实现】

  • 初始化一个数栈【用于存储数字】,初始化一个符号栈【用于存储操作符和括号】

  • 从左往右扫描表达式,取出每一个字符,然后开始判断是数字类型还是符号类型。

  • 数栈
    入栈规则:
    如果是数字,理论上直接入栈,但是考虑第一个元素是负数和数字多位数情况,这里加入了一个判断语句和循环语句进行处理。

  • 符号栈
    入栈规则:
    ~ 为空:无论操作符是左括号还是什么操作符,都直接入栈
    ~非空:比较符号优先级

    • 左括号直接入栈
    • 右括号时,则弹出操作符栈栈顶操作符,同时弹出数栈中两个元素,进行运算并压入数栈中,然后弹出左括号,右括号也不要入栈,直接匹配消失。
    • 入栈操作符优先级大于栈顶操作符优先级,直接入栈
    • 入栈操作符优先级小于等于栈顶操作符优先级,从数栈弹出两个元素和栈顶操作符计算,结果入数栈,然后将操作符入符号栈。

注意:运算时,用后弹出的数【栈顶下一个元素】操作前面弹出的数【栈顶元素】

package test;
import datastructure.SimpleCalc;
/**
 * @author calarqiang
 * @create 2022-04-24-10:20
 * 测试简易计算器
 */
public class SimpleCalcTest {
    public static void main(String[] args) {
//        定义一个数栈和一个操作符栈
        SimpleCalc num = new SimpleCalc(10);
        SimpleCalc oper = new SimpleCalc(10);
//        定义要计算的表达式, 有一个更简单的方法,根本就不需要这么复杂,直接将表达式以空格隔开,然后分割存入数组中,再遍历
        String str = "-5+12-2";
//        存放获取表达式的单个值
        char elem = ' ';
//        存放数栈栈顶弹出的第一个元素
        int num1;
//        存放符号栈栈顶弹出的第二个元素
        int num2;
//        存放符号栈弹出的符号
        char ch;
//        存放最后运算结果
        int res = 0;

        for (int index = 0; index < str.length(); index++) {
//            定义一个字符串来存储数字
            String strNum = "";
            elem = str.charAt(index);
//            解决第一个数是负数的情况
            if (index == 0 && SimpleCalc.priority(str.charAt(index)) == 0) {
                strNum += elem;
                index++;
                elem = str.charAt(index);
            }

//             判断第一个数或者说如果第一个数是负数,就判断第二个数
            if (isNum(elem)) {

                strNum += elem;
//                如果是数字,先判断后面有几个数字,这样可以解决多位数的情况
                while (index != str.length() - 1) {
                    if (isNum(str.charAt(index + 1))) {
                        strNum += str.charAt(++index);
                    } else {
                        break;
                    }
                }

                num.push(Integer.parseInt(strNum));
//                num.push(elem-48);
            } else if (isLeftBracket(elem)) {
//                如果是左括号,直接入栈
                oper.push(elem);
            } else if (isRightBracket(elem)) {
//                如果是右括号,则弹出符号栈符号,与数栈运算,直到遇到左括号
                ch = (char) oper.pop();
                num1 = num.pop();
                num2 = num.pop();
//                将计算结果入数栈
                num.push(SimpleCalc.calc(num1, num2, ch));
//                弹出左括号,与右括号抵消
                oper.pop();
            } else {
//                这里表示入栈的是符号
//                如果符号为空,就直接入栈
                if (oper.isEmpty()) {
                    oper.push(elem);
                } else {
//                如果符号栈不空,且入栈符号优先级比栈顶符号优先级大,则直接入栈
                    if (SimpleCalc.priority(elem) > SimpleCalc.priority(oper.peek())) {
                        oper.push(elem);
                    } else {
//                如果符号栈不空,但入栈符号优先级比栈顶符号优先级小或者相等,则需要进行运算
                        num1 = num.pop();
                        num2 = num.pop();
                        ch = (char) oper.pop();
                        num.push(SimpleCalc.calc(num1, num2, ch));
//                        然后将符号入栈
                        oper.push(elem);
                    }
                }
            }
        }
//        操作完成后,判断符号栈是否为空,如果为空,就证明此时数字栈中只存在一个结果,如果不空,就要进行运算
        while (!oper.isEmpty()) {
//            开始运算
            num1 = num.pop();
            num2 = num.pop();
            ch = (char) oper.pop();
            num.push(SimpleCalc.calc(num1, num2, ch));
        }
//        这里就可以输出数字栈的最后结果了
        res = num.pop();
        System.out.println("最后结果是:" + res);
    }

    public static boolean isNum(char ch) {
        return (ch >= '0' && ch <= '9') ? true : false;
    }

    public static boolean isLeftBracket(char ch) {
        return ch == '(';
    }

    public static boolean isRightBracket(char ch) {
        return ch == ')';
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值