数组实现栈、中缀计算器、逆波兰后缀计算器、 中缀转后缀完整版本!

栈的介绍:

  1. 栈的英文为(stack)
  2. 栈是一个先入后出(FILO-First In Last Out)的有序列表。
  3. 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的
    一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
  4. 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元
    素最先删除,最先放入的元素最后删除
  5. 图解方式说明出栈(pop)和入栈(push)的概念
    数组实现栈、中缀计算器、逆波兰后缀计算器、 中缀转后缀完整版本!_第1张图片

栈的快速入门

  1. 用数组模拟栈的使用,由于栈是一种有序列表,当然可以使用数组的结构来储存栈的数据内容,
    下面我们就用数组模拟栈的出栈,入栈等操作。
  2. 实现思路分析,并画出示意图
    数组实现栈、中缀计算器、逆波兰后缀计算器、 中缀转后缀完整版本!_第2张图片

【数据结构与算法】逆波兰表达式、波兰表达式

栈的计算器
  • 前缀表达式 (波兰表达式 从右往左扫描 )
  • 中缀表达式 (正常人计算方式)
  • 后缀表达式 (逆波兰表达式 从左往右)

1.前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前。比如:- × + 3 4 5 6
2.中缀表达式就是常见的运算表达式,如(3+4)×5-6
3.后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后,比如:3 4 + 5 × 6

然后我们还需明确一些概念,下面通过我们最熟悉的中缀表达式画出一棵语法树来直观认识一下前后缀表达式的生成。以A+B*(C-D)-E*F为例:
数组实现栈、中缀计算器、逆波兰后缀计算器、 中缀转后缀完整版本!_第3张图片

中缀表达式得名于它是由相应的语法树的中序遍历的结果得到的。上面的二叉树中序遍历的结果就是A+B*(C-D)-E*F。
前缀表达式是由相应的语法树的前序遍历的结果得到的。上图的前缀表达式为- + A * B - C D * E F
后缀表达式又叫做逆波兰式。它是由相应的语法树的后序遍历的结果得到的。上图的后缀表达式为:A B C D - * + E F * -


下面我们关注几个点:

  1. 如何用数组实现一个栈
  2. 如果用中缀表达式求出运算结果
  3. 如何根据一个逆波兰表达式求出运算结果?
  4. 如果将一个中缀表达式转换成后缀表达式(逆波兰表达式)
  
/**
 * 数组实现栈, 中缀计算器,中缀转后缀, 逆波兰后缀表达式实现
 */
public class ArrayStackDemo {

    public static void main(String[] args) {
        /** 测试栈*/
        //testStack();

        /** 实现计算器 (中缀表达式)*/
        //calculator();

        /** 逆波兰表达式实现*/
        //polan();

        /** 中缀表达式转换后缀表达式*/
        backMiddle();
    }

    /**
     * 中缀转后缀测试
     */
    private static void backMiddle() {
        /**
         * 1+((2+3)×4)-5 => 转成  1 2 3 + 4 * + 5 –
         * 思路:
         * 1、把中缀表达式 转成 转成字符串存在list里面
         * 2、1)创建一个栈 存放运算符
         *    2)创建一个栈 存放数据(由于这个栈只是添加不取,并且还要依次打印出来,所以改成list集合)
         *    3)遍历中缀list
         *       3.1 如果数数直接添加数据list中
         *       3.2 如果运算符栈为空 或者 栈顶为 “(”直接入栈
         *       3.3 如果运算符栈顶为“)”,依次取出放入数据集合中,直到遇到“(”为止, 并且把“(”取出
         *       3.4 以上3种都不满足:
         *           如果运算级大于栈顶直接入运算符栈
         *           如果运算级小于等于栈顶,把运算符栈定数据取出来 放入数据集合中,直到遇到大于或者“(”括号不是算符返回,
         *           然后再把这运算符放入运算符栈中
         *  3、把运算符栈中数据依次放入数据集合中。
         *  4、遍历集合集合出结果
         */
        String expression = "1+((2+3)*4)-5";
        // 把中缀表达式放入list中
        ArrayList list = getChangeList(expression);
        // 转换
        ArrayList expssion = getExpssion(list);
        System.out.print("中缀:" + expression + "  转后缀:" + expssion);

    }

    /**
     * 中缀转后缀过程
     */
    private static ArrayList getExpssion(ArrayList list) {
        // 创建运算符栈
        Stack operStack = new Stack<>();
        ArrayList numList = new ArrayList<>();
        for(int i = 0; i < list.size(); i++) {
            String item = list.get(i);
            if(item.matches("\\d")) {
                // 如果是数直接加入数栈
                numList.add(item);
            }else if(item.equals("(") || operStack.isEmpty()) {
                operStack.push(item);
            }else if(item.equals(")")) {
                // 在符号栈取出符号放入  数栈中 直到遇到(
                while (!operStack.peek().equals("(")) {
                    numList.add(operStack.pop());
                }
                // 取出 (
                operStack.pop();
            }else {
                // 这里面是运算符
                if(getValue(item) > getValue(operStack.peek())) {
                    // 大于栈顶优先级直接加入
                    operStack.push(item);
                }else {
                    // 取出栈里的来。放入数栈,再把自己放入数栈
                    while (operStack.size() != 0 && getValue(operStack.peek()) <= getValue(item)){
                        numList.add(operStack.pop());
                    }
                    // 把自己放进去
                    operStack.push(item);
                }
            }
        }

        // 遍历运算符栈 放入 list中
        for(int i = 0; i < operStack.size(); i++) {
            numList.add(operStack.pop());
        }
        return numList;
    }

    /**
     * 返回运算符等级
     */
    public static int getValue(String oper){
        if("+".equals(oper) || "-".equals(oper)) {
            return 1;
        }else if("*".equals(oper) || "/".equals(oper)) {
            return 2;
        }else {
            return 0;
        }
    }

    /**
     * 后缀转换list集合
     */
    private static ArrayList getChangeList(String expression) {
        ArrayList list = new ArrayList<>();
        String num = "" ;
        for(int i = 0; i < expression.length(); i++) {
            /**
             * 0 -9  48 49 50 51 52 53 54 55 56 57
             * */
            if(expression.charAt(i) < 48 || expression.charAt(i) > 57 ) {
                // 说明是运算符直接加入姐
                list.add(expression.charAt(i) + "");
            }else {
                // 说明是数字,但是要考虑是多位数情况
                num += expression.charAt(i);
                if(expression.charAt(i) >= 48 && expression.charAt(i) <= 57) {
                    list.add(num);
                    num = "";
                }
            }
        }
        System.out.print(list);
        return list;
    }

    /**
     *  逆波兰表达式(后缀表达式)
     */
    private static void polan() {
        //String suffixExpression = "(3+4) * 5 - 6 ";
        String suffixExpression = "3 4 + 5 * 6 - ";
        //思路
        //1. 先将 "3 4 + 5 × 6 - " => 放到ArrayList中
        //   案例2:4 * 5 - 8 + 60 + 8 / 2 ==> 逆波兰: 4 5 * 8 - 60 + 8 2 / +
        //   以案例2看出 扫描到8/2的时候是一个树,也就是说优先级高的先加进去。
        //   但是计算方法不算。数组直接入栈,遇到运算符取出2个数运算结果,在放入数栈,直到遍历完成数栈剩下一个。
        //2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算

        List list = getListString(suffixExpression);
        System.out.println("rpnList=" + list);
        int res = calculate(list);
        System.out.println("计算的结果是=" + res);
    }

    /**
     * 计算结果返回
     * 1、遍历list  判断是数的直接入数栈
     * 2、如果是运算符取出两个数计算结果,然后在入数栈,直到剩下最后一个结果
     */
    private static int calculate(List list) {
        int result =  0;
        Stack stack = new Stack<>();
        for(int i = 0; i < list.size(); i++) {
            String data = list.get(i);
            // 正则表达式, 取出来的数  \d :匹配数字,包括0~9;
            if(data.matches("\\d")) {
                stack.push(Integer.parseInt(data));
            }else {
                int num1 = stack.pop();
                int num2 = stack.pop();
                if(data.equals("+")) {
                    result = num1 + num2;
                }else if(data.equals("-")) {
                    result = num2 - num1;
                }else if(data.equals("*")) {
                    result = num1 * num2;
                }else if(data.equals("/")) {
                    result = num2 / num1;
                }else {
                    System.out.print("符号有问题~~");
                }
                stack.push(result);
            }
        }
        return result;
    }

    /**
     * 截取成list返回
     */
    private static List getListString(String str) {
        ArrayList list = new ArrayList<>();
        String[] strArr = str.split(" ");
        for (String data : strArr){
            list.add(data);
        }
        return list;
    }

    /**
     * 中缀表达式计算过程
     */
    private static void calculator() {
        /**
         * 思路:
         * 1、创建两个栈,一个为数栈,一个为运算符栈
         * 2、获取的数据判断是不是运算符,如果不是运算符直接入数栈,
         *    如果是运算符:
         *    1) 优先级小于等于当前运算符号栈,在数栈中取出两个数,运算符栈中取出一个做运算,把结果在放入数栈中
         *    2) 优先级大于运算符栈顶,直接放入符号栈
         *
         * 3、遍历取出数据以后,把数栈和运算符栈中数据 取出计算,把结果放入数栈中,直到数栈结构为1个就是结果。
         *
         *  注意: 如果是多位数,入栈会有问题。判断条件是下一个为运算符 才入栈。 期间用变量加一下。
         */
        ArrarStack numStack = new ArrarStack(15);
        ArrarStack operStack = new ArrarStack(15);

        String expression = "4*2-5+4+1";
        // 数栈取的数据2
        int num1 = 0;
        // 数栈取的数据2
        int num2 = 0;
        // 运算符
        int oper = 0;
        // 取出的索引
        int index = 0;
        // 定义变量
        String keepNum = "";
        while (true){
            // 左开右闭,截取字符串,取出第一个字符
            char ch = expression.substring(index, index + 1).charAt(0);
            if(operStack.isOper(ch)) {
                if(operStack.isEmpty()) {
                    // 为空直接入栈
                    operStack.push(ch);
                }else {
                    // 判断上一个判断运算级(注意不是取出)
                    if(operStack.priority(ch) > operStack.priority(operStack.peek())) {
                        // 运算符大于直接如运算符栈
                        operStack.push(ch);
                    }else {
                        // 取出数栈两个,运算符一个,把计算结果在放入数栈中
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        int result = operStack.result(num1, num2, oper);
                        numStack.push(result);
                        // 当前符号符入栈
                        operStack.push(ch);
                    }
                }
            }else {
                // 直接入栈,注意有可能是多位数,这样加进去就不对了。所以加的时机应该是下一个是运算符在入栈
                keepNum += ch;
                if(index == expression.length() - 1) {
                    // 最后一个直接入数栈
                    numStack.push(Integer.parseInt(keepNum));
                    break;
                }
                char str = expression.substring(index + 1, index + 2).charAt(0);
                if(numStack.isOper(str)) {
                    // 下一个数运算符入栈
                    numStack.push(Integer.parseInt(keepNum));
                    keepNum = "";
                }
            }
                index++;

        }

        //当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
        while (true){
            if(operStack.isEmpty()) {
                break;
            }

            // 取出数栈两个,运算符一个,把计算结果在放入数栈中
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            int result = operStack.result(num1, num2, oper);
            numStack.push(result);
        }

        System.out.print("运算结果:" + numStack.pop());


    }

    private static void testStack() {
        ArrarStack stack = new ArrarStack(3);
        Scanner scanner = new Scanner(System.in);
        String key = "";
        boolean loop = true;

        while (loop){
            System.out.println("show: 表示显示栈");
            System.out.println("exit: 退出程序");
            System.out.println("push: 表示添加数据到栈(入栈)");
            System.out.println("pop: 表示从栈取出数据(出栈)");
            System.out.println("请输入你的选择");
            key = scanner.next();

            switch (key) {
                case "s":
                    stack.showStack();
                    break;
                case "push":
                    System.out.println("请输入一个数");
                    int value = scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    try {
                        int res = stack.pop();
                        System.out.printf("出栈的数据是 %d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case "e":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
    }
}

class ArrarStack{
    private int maxSize;
    private int[] mStack;
    // 栈帧
    private int top = -1;

    public ArrarStack(int maxSize) {
        this.maxSize = maxSize;
        mStack = new int[maxSize];
    }

    /**
     * @param num1  数栈顶1
     * @param num2  数栈顶2
     * @param oper  运算符栈顶1
     * @return
     */
    public int result(int num1, int num2, int oper){
        if(oper == '+') {
            return num1 + num2;
        }else if(oper == '-') {
            return num2 - num1;
        }else if(oper == '*') {
            return num1 * num2;
        }else if(oper == '/') {
            return num2 / num1;
        }else {
            return 0;
        }
    }

    /**
     * 是不是运算符
     */
    public boolean isOper(char ch){
       if(ch == '+') {
           return true;
       }else if(ch == '-') {
           return true;
       }else if(ch == '*') {
           return true;
       }else if(ch == '/') {
           return true;
       }else {
           return false;
       }
    }

    /**
     * 返回优先级
     */
    public int priority(int oper){
        if(oper == '+' || oper == '-') {
            return 1;
        }else if(oper == '*' || oper == '/') {
            return 2;
        }else {
            return -1;
        }
    }

    /**
     * 查询栈顶数据
     */
    public int peek(){
        return mStack[top];
    }

    /**
     * 入栈
     */
    public void push(int data){
        if(isFull()) {
            System.out.println("栈已满~~~");
            return;
        }

        top++;
        mStack[top] = data;
    }

    /**
     * 出栈
     */
    public int pop(){
        if(isEmpty()) {
            throw new RuntimeException("栈为空~~");
        }

        int value = mStack[top];
        top--;
        return value;
    }

    /**
     * 空没空
     */
    public boolean isEmpty(){
        return top == -1;
    }

    /**
     * 满没满
     */
    public boolean isFull(){
        if(top == maxSize - 1) {
            return true;
        }else {
            return false;
        }
    }

    /**
     * 查询栈
     */
    public void showStack(){
        if(isEmpty()) {
            throw new RuntimeException("栈为空~~");
        }

        for(int i = top; i >= 0; i--) {
            System.out.println(mStack[i]);
        }
    }

}

你可能感兴趣的:(技术分析,数据结构)