中缀、前缀、后缀表达式的运算

  中缀表达式,就是在表达式中,操作符在操作数的中间,比如 (1+2)*3,+和*在1, 2, 3的中间。前缀表达式,就是操作符在操作数的前面,比如 +12,+在1, 2的前面。后缀表达式,就是操作符在操作数的后面,比如 12+,+在1, 2的后面。为什么会有这么多表达式呢?它们目的不同。中缀表达式,便于我们书写,也符合我们的阅读习惯,在计算机程序中,都是写中缀表达式,但它却不利于计算机进行算术计算,因为涉及到优先级和括号。前缀表达式和后缀表达式中没有括号,并且运算符的优先级也通过它们在表达式中的顺序体现出来了,有利于计算机进行算术运算。前缀表达式也叫波兰表达式,因为它是由一个波兰人发明的,相应的,后缀表达式也称为逆波兰表达式。举个例子来说明一下三者的运算过程,假设计算(3+4)*5-6

   使用中缀表达式进行计算

  1,创建两个栈,一个是数字栈,用来存放操作数,一个是字符栈,用来存放操作符。

  2,算术表达式是一个字符串,从左到右循环遍历表达式,依次取出每一个字符。

    当取出的字符是操作数时,入数字栈。

    当取出的字符是操作符时,这时还要看操作符的优先级和字符栈是否为空

      如果字符栈为空,直接把取出的字符放入到字符栈中。

      如果字符栈不为空,则要比较字符的优先级

        如果取出的字符的优先级大于等于字符栈中栈顶的字符的优先级,直接把取出的字符放入到字符栈中。

        如果取出的字符的优先级比字符栈中栈顶的字符的优先级低,则要循环进行如下操作,直到取出的字符的优先级大于等于字符栈中栈顶字符的优先级或者栈为空,此时,把取出的字符放入到字

        符栈中。如下操作就是:

            1,从字符栈中弹出操作符

            2,从数字栈中弹出两个操作数。

            3,操作数结合操作符进行计算,要注意操作数顺序,后面pop出的数在求值的表达式中是前面的操作数,尤其是在做减法的时候

            4,计算出的值放入到数字栈。

        为什么要比较操作符的优先级呢?因为要确定操作数属于哪个操作符,相邻两个操作符之间的数字是共享的。2+3*5. 3属于+,也属于*

    当取出的字符是( 时,直接入字符栈,因为要等到), 左括号本身不能决定计算的顺序

    当取出的字符是)时,那就要循环执行如下操作,直到碰到(。此后把(弹出来,舍弃掉

        1,从字符栈中弹出操作符

        2,从数字栈中弹出两个操作数。

        3,操作数结合操作符进行计算,要注意操作数顺序,后面pop出的数在求值的表达式中是前面的操作数,尤其是在做减法的时候

        4,计算出的值放入到数字栈。

  3,表达式循环完毕后,如果字符栈不为空,还是执行如下操作,直到栈为空

    1,从字符栈中弹出操作符

    2,从数字栈中弹出两个操作数。

    3,操作数结合操作符进行计算,要注意操作数顺序,后面pop出的数在求值的表达式中是前面的操作数,尤其是在做减法的时候

    4,计算出的值放入到数字栈。

  4,字符栈为空,数字栈中只剩下一个数字,这个数字就是表达式的值。

  看一下具体的实现过程,两个栈number, operator,Java中有Stack类。怎么判断字符是数字,Java中有 Character.isDigit() ,如果没有这个函数也没有关系,只要一个字符>'0' 且小于‘9’ 就可以

private static boolean isDigit(char c){
    return c >='0' && c <= '9';
}

  怎么判断是操作符呢,假设只计算加减乘除,只要字符是‘+’或‘-’ 或‘*'或/ 就可以

    private static boolean isOperator(char value) {
        return value == '*' || value == '/' || value == '+' || value == '-';
    }

  优先级呢?用数字表示吧,数字越大,优先级越高

// 数字越大,优先级越高。
private static int priority(char operator) {
    if(operator == '*' || operator == '/' ) {
        return 1;
    } else if (operator == '+' || operator == '-' ) {
        return 0;
    } else {
        return -1;
    }
}

  最后定义一下操作,假设数字栈就是number,字符栈就是operator,操作就是

    private static void performOperation(Stack<Integer> numbers, Stack<Character> operators) {
        int num1 = numbers.pop();
        int num2 = numbers.pop();
        char operator = operators.pop();

        int result = 0;
        switch (operator) {
            case '+': {
                result = num2 + num1;
                break;
            }
            
            case '-': {
                result = num2 - num1;
                break;
            }
            
            case '*': {
                result = num2 * num1;
                break;
            }
            
            case '/': {
                result = num2 / num1;
                break;
            }
        }
        
        numbers.push(result);
    }
}

  那整个表达式的运算过程就是

public static int evaluate(String expression){
        // 创建数字栈和字符栈
        Stack<Integer> numbers = new Stack<>();
        Stack<Character> operators = new Stack<>();

        // 循环遍历表达式
        for (int i = 0; i < expression.length(); i++) {
            // 取出每一个字符
            char c = expression.charAt(i);

            // 如果取出的字符是操作数, 转化成数字,入数字栈
            if (Character.isDigit(c)){
                numbers.push(Integer.parseInt(String.valueOf(c)));
            }
            // 取出的字符是操作符
            else if(isOperator(c)) {
                if (operators.isEmpty()){ // 如果栈为空
                    operators.push(c);
                } else if (priority(c) >= priority(operators.peek())){ // 优先级高
                    operators.push(c);
                } else {
                    while (!operators.isEmpty() && priority(c) < priority(operators.peek())){ // 优先级低
                        performOperation(numbers, operators);
                    }
                    operators.push(c);
                }
            }
            // 左括号直接入栈
            else if (c== '('){
                operators.push(c);
            } 
            // 右括号时,依次弹栈,直到遇到左括号
            else if(c == ')'){
                while (operators.peek() != '('){
                    performOperation(numbers, operators);
                }
                // 舍弃左括号
                operators.pop();
            } else {
                // 其他符号,什么都不做,也可以continue
                continue;
            }
        }

        // 循环完毕后,如果字符栈不为空
        while (!operators.isEmpty()){
            performOperation(numbers, operators);
        }

        return numbers.pop();
    }

  使用前缀表达式进行计算

  (3+4)*5-6的前缀表达式是-*+3456,计算规则如下

  1,创建一个数字栈,用于存放数字

  2,从右向左扫描,遇到数字,将数字压入到栈中,遇到操作符时,弹出栈顶的两个数,用操作符对它们做相应的计算,并将结果入栈

  3,重算上述过程直到表达式的最左端,最后运算出的值即为表达式的结果

  从右向左扫描,将6 5 4 3 压入栈中,遇到+, 弹出3和4,并计算3+4,得到7,将7入栈,再运到*, 弹出7和5,计算7*5 =35,将35入栈,最后-, 将35和6弹出,35-6 = 29,已经扫描到最左边了,所以29就是最后的值。要注意操作数顺序,pop出的数和它在求值的表达式中的顺序是一致的。

public static int prefixEvaluation(String expression) {
        // 创建数字栈
        Stack<Integer> numbers = new Stack<>();

        // 循环遍历表达式
        for (int i = expression.length() - 1; i >= 0; i--) {
            // 取出每一个字符
            char c = expression.charAt(i);

            // 如果取出的字符是操作数, 转化成数字,入数字栈
            if (Character.isDigit(c)){
                numbers.push(Integer.parseInt(String.valueOf(c)));
            } else { // 取出的字符是操作符,那就pop 出两个数,进行计算,再入栈
                int num1 = numbers.pop();
                int num2 = numbers.pop();
                int result = 0;
                // 操作符,加减,乘除
                if(c == '+') {
                    result = num1 + num2;
                } else if (c == '-') {
                    result = num1 - num2;
                } else if (c == '*') {
                    result = num1 * num2;
                } else {
                    result = num1 / num2;
                }

                numbers.push(result);
            }
        }

        return numbers.pop();
    }

  使用后缀表达式进行计算

   (3+4)*5-6   对应的后缀表达式 3 4 + 5 * 6 - 

  后缀表达式的算法和前缀表达式一样,只不过是从左向右遍历表达式

  1,创建一个数字栈,用于存放数字

  2,从左向右扫描表达式,遇到数字,将数字压入到栈中,遇到操作符时,弹出栈顶的两个数,用操作符对它们做相应的计算,并将结果入栈

  3,重算上述过程直到表达式的最右端,最后运算出的值即为表达式的结果

  3,4入栈,遇到+号,3,4出栈,进行相加,得到7,将7入栈,遇到5,入栈,遇到*,弹出7和5,相乘,得到35,入栈遇到6,将其入栈,遇到-号,弹出35和6,进行相减,得出29。由于到了表达式的最右边,29就是结果了。

public static int suffixEvaluation(String expression) {
        // 创建数字栈
        Stack<Integer> numbers = new Stack<>();

        // 循环遍历表达式
        for (int i = 0; i < expression.length(); i++) {
            // 取出每一个字符
            char c = expression.charAt(i);

            // 如果取出的字符是操作数, 转化成数字,入数字栈
            if (Character.isDigit(c)){
                numbers.push(Integer.parseInt(String.valueOf(c)));
            } else { // 取出的字符是操作符,那就pop 出两个数,进行计算,再入栈
                int num1 = numbers.pop();
                int num2 = numbers.pop();
                int result = 0;
                // 操作符,加减,乘除
                if(c == '+') {
                    result = num2 + num1;
                } else if (c == '-') {
                    result = num2 - num1;
                } else if (c == '*') {
                    result = num2 * num1;
                } else if(c == '/') {
                    result = num2 / num1;
                }

                numbers.push(result);
            }
        }

        return numbers.pop();
    }

 

上一篇:如何使用 Numbers 制作编辑精美图表、互动式图表?


下一篇:JS数组中 forEach() 和 map() 的区别