数据结构与算法——栈实现计算器

中缀表达式计算器

在这里插入图片描述

public class Calculator {

    @Test
    public void testCalculator() {

        String expression = "30+2*6-2";

        //创建两个栈
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(10);

        //定义相关变量
        int index = 0;  //用于扫描
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';  //将每次扫描得到的char保存到ch
        String keepNum = ""; //用于拼接多位数

        //while循环扫描expression
        while (true) {

            //依次得到expression的每一个字符
            ch = expression.substring(index, index + 1).charAt(0);

            //判断ch是什么
            if (operStack.isOper(ch)) {   //是运算符
                //判断符号栈是否为空
                if (!operStack.isEmpty()) {   //不为空
                    //比较优先级,若当前操作符优先级小于等于栈中操作符
                    if (operStack.priority(ch) <= operStack.priority(operStack.peek())) {
                        //数栈pop两个数,符号栈pop一个符号,运算
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = numStack.cal(num1, num2, oper);
                        //运算结果入数栈a
                        numStack.push(res);
                        //当前操作符入符号栈
                        operStack.push(ch);
                    } else {
                        //如果当前操作符的优先级大于栈中的操作符,就直接入符号栈
                        operStack.push(ch);
                    }
                } else { //符号栈为空
                    operStack.push(ch);
                }
            } else { //是数字
                //numStack.push(ch - '0');
                //处理多位数
                keepNum += ch;

                //如果ch是最后一位,直接入栈
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepNum));
                } else {

                    //判断下一个字符是不是数字,是数字继续扫描,是运算符则入栈
                    if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
                        //是运算符则入栈
                        numStack.push(Integer.parseInt(keepNum));
                        keepNum = "";   //清空
                    }

                }
            }

            //判断是否扫描到最后
            index++;
            if (index >= expression.length()) {
                break;
            }

        }

        while (true) {
            if (operStack.isEmpty()) {
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1, num2, oper);
            numStack.push(res);
        }

        System.out.println("表达式" + expression + "的结果为:" + numStack.pop());

    }

}

//创建栈
class ArrayStack2 {

    private int maxSize;    //栈的大小
    private int[] stack;    //数组模拟栈,数据存放在该数组中
    private int top = -1;   //top表示栈顶,初始化为-1

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

    //查看栈顶的值
    public int peek() {
        return stack[top];
    }

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

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

    //入栈-push
    public void push(int value) {
        if (isFull()) {
            System.out.println("栈满");
            return;
        }
        stack[++top] = value;
    }

    //出栈-pop
    public int pop() {
        if (isEmpty())
            throw new RuntimeException("栈空,没有数据");
        return stack[top--];
    }

    //显示栈(遍历),需要从栈顶开始显示数据
    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]);
        }

    }

    /**
     * 返回运算符的优先级,数字越大,优先级越高
     *
     * @param oper 运算符
     * @return 优先级
     */
    public int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 0;
        } else {
            return -1;
        }
    }

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

    //计算方法
    public int cal(int num1, int num2, int oper) {

        int res = 0;    //存放计算结果

        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
        }

        return res;

    }

}

逆波兰计算器

输入一个中缀表达式,转换为后缀表达式后进行运算。

中缀表达式转为相应的List形式

    public static List<String> toInfixExpressionList(String s) {

        //定义一个List存放中缀表达式的内容
        List<String> ls = new ArrayList<>();
        int i = 0;  //指针,用于遍历中缀表达式字符串
        String str; //用于拼接多位数
        char c;     //每遍历到一个字符就放入到c

        do {
            //如果c是非数字,就加入到ls中
            if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {
                ls.add("" + c);
                i++;
            } else { //如果是数字,考虑多位数
                str = "";    //str置空
                while (i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57) {
                    str += c;   //  拼接
                    i++;
                }
                ls.add(str);
            }

        } while (i < s.length());

        return ls;

    }

中缀表达式转换为后缀表达式

  • 中缀表达式转换为后缀表达式的步骤
  1. 初始化两个栈:运算符栈 s1 和储存中间结果的栈 s2;
  2. 从左至右扫描中缀表达式;
  3. 遇到操作数时,将其压 s2;
  4. 遇到运算符时,比较其与 s1 栈顶运算符的优先级:
    (1).如果 s1 为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
    (2).否则,若优先级比栈顶运算符的高,也将运算符压入 s1;
    (3).否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到4.1与 s1 中新的栈顶运算符相比较;
  5. 遇到括号时:
    (1) 如果是左括号“(”,则直接压入 s1
    (2) 如果是右括号“)”,则依次弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这一对括号丢弃
  6. 重复步骤 2 至 5,直到表达式的最右边
  7. 将 s1 中剩余的运算符依次弹出并压入 s2
  8. 依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
    在这里插入图片描述
    //中缀表达式的List转换为后缀表达式的List
    public static List<String> parseSuffixExpressionList(List<String> ls) {

        //定义两个栈
        Stack<String> s1 = new Stack<>();   //符号栈
        //s2无pop操作,用List即可
        List<String> s2 = new ArrayList<>();    //存储中间结果的List

        //遍历ls
        for (String item : ls) {

            //如果是数,加入s2
            if (item.matches("\\d+")) {
                s2.add(item);
            } else if (item.equals("(")) {
                s1.push(item);
            } else if (item.equals(")")) {
                //如果是右括号,则一次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将括号丢弃
                while (!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();   //将(弹出
            } else {
                //当item的优先级小于等于s1栈顶的运算符的优先级,将s1栈顶的运算符弹出压入s2内,再次与s1新的栈顶运算符比较
                while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
                    s2.add(s1.pop());
                }
                //item入栈
                s1.push(item);
            }

        }

        //s1中剩余的运算符依次弹出并加入s2
        while (s1.size() != 0) {
            s2.add(s1.pop());
        }

        return s2;  //因为是存放到List,因此按顺序输出就是对应后缀表达式的List

    }

运算后缀表达式

例如: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 - , 针对后缀表达式求值步骤如下:

  1. 从左至右扫描,将 3 和 4 压入堆栈;
  2. 遇到+运算符,因此弹出 4 和 3(4 为栈顶元素,3 为次顶元素),计算出 3+4 的值,得 7,再将 7 入栈;
  3. 将 5 入栈;
  4. 接下来是×运算符,因此弹出 5 和 7,计算出 7×5=35,将 35 入栈;
  5. 将 6 入栈;
  6. 最后是-运算符,计算出 35-6 的值,即 29,由此得出最终结果
    //运算逆波兰表达式
    public static int calculate(List<String> ls) {

        //创建一个栈
        Stack<String> stack = new Stack<>();

        //遍历ls
        for (String item : ls) {

            //使用正则表达式取出数
            if (item.matches("\\d+")) {   //匹配多位数
                //入栈
                stack.push(item);
            } else {
                //pop出两个数并运算
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                switch (item) {
                    case "+":
                        res = num1 + num2;
                        break;
                    case "-":
                        res = num1 - num2;
                        break;
                    case "*":
                        res = num1 * num2;
                        break;
                    case "/":
                        res = num1 / num2;
                        break;
                }
                //res入栈
                stack.push("" + res);
            }

        }

        //最后留在栈里的是运算结果
        return Integer.parseInt(stack.pop());

    }

整体代码

package stack;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PolandNotation {

    @Test
    public void testPolandNotation() {

        //中缀表达式转后缀表达式 1+((2+3)*4)-5 转成 1 2 3 + 4 * + 5 -
        //1. 将中缀表达式转换为List [1,+,(,(,2,+,3,),*,4,),-,5]
        //2. 中缀表达式的List转换为后缀表达式的List[1,2,3,+,4,*,+,5,-]
        //3. 运算后缀表达式
        String expression = "1+((2+3)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("中缀表达式的List = " + infixExpressionList);
        List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
        System.out.println("后缀表达式的List = " + suffixExpressionList);

        System.out.println("expression = " + calculate(suffixExpressionList));


        //定义逆波兰表达式 (3+4)*5-6
//        String suffixExpression = "3 4 + 5 * 6 -";
//        List<String> list = getListString(suffixExpression);
//        System.out.println(calculate(list));

    }


    //将中缀表达式转为List的方法
    public static List<String> toInfixExpressionList(String s) {

        //定义一个List存放中缀表达式的内容
        List<String> ls = new ArrayList<>();
        int i = 0;  //指针,用于遍历中缀表达式字符串
        String str; //用于拼接多位数
        char c;     //每遍历到一个字符就放入到c

        do {
            //如果c是非数字,就加入到ls中
            if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {
                ls.add("" + c);
                i++;
            } else { //如果是数字,考虑多位数
                str = "";    //str置空
                while (i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57) {
                    str += c;   //  拼接
                    i++;
                }
                ls.add(str);
            }

        } while (i < s.length());

        return ls;

    }


    //中缀表达式的List转换为后缀表达式的List
    public static List<String> parseSuffixExpressionList(List<String> ls) {

        //定义两个栈
        Stack<String> s1 = new Stack<>();   //符号栈
        //s2无pop操作,用List即可
        List<String> s2 = new ArrayList<>();    //存储中间结果的List

        //遍历ls
        for (String item : ls) {

            //如果是数,加入s2
            if (item.matches("\\d+")) {
                s2.add(item);
            } else if (item.equals("(")) {
                s1.push(item);
            } else if (item.equals(")")) {
                //如果是右括号,则一次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将括号丢弃
                while (!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();   //将(弹出
            } else {
                //当item的优先级小于等于s1栈顶的运算符的优先级,将s1栈顶的运算符弹出压入s2内,再次与s1新的栈顶运算符比较
                while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
                    s2.add(s1.pop());
                }
                //item入栈
                s1.push(item);
            }

        }

        //s1中剩余的运算符依次弹出并加入s2
        while (s1.size() != 0) {
            s2.add(s1.pop());
        }

        return s2;  //因为是存放到List,因此按顺序输出就是对应后缀表达式的List

    }


    //运算逆波兰表达式
    public static int calculate(List<String> ls) {

        //创建一个栈
        Stack<String> stack = new Stack<>();

        //遍历ls
        for (String item : ls) {

            //使用正则表达式取出数
            if (item.matches("\\d+")) {   //匹配多位数
                //入栈
                stack.push(item);
            } else {
                //pop出两个数并运算
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                switch (item) {
                    case "+":
                        res = num1 + num2;
                        break;
                    case "-":
                        res = num1 - num2;
                        break;
                    case "*":
                        res = num1 * num2;
                        break;
                    case "/":
                        res = num1 / num2;
                        break;
                }
                //res入栈
                stack.push("" + res);
            }

        }

        //最后留在栈里的是运算结果
        return Integer.parseInt(stack.pop());

    }


      //将一个逆波兰表达式的数据和运算符放入ArrayList中
//    public static List<String> getListString(String suffixExpression) {
//        //分割suffixExpression
//        String[] split = suffixExpression.split(" ");
//        List<String> list = new ArrayList<>();
//        for (String ele : split) {
//            list.add(ele);
//        }
//        return list;
//        //return new ArrayList<>(Arrays.asList(split));
//    }

}


class Operation {

    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 2;
    private static int DIV = 2;

    //返回优先级数字的方法
    public static int getValue(String operation) {
        int result = 0;
        switch (operation) {
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                System.out.println("不存在该运算符");
                break;
        }
        return result;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值