Summary of prefix, infix, postfix expressions and simple operations


title: Prefix, infix, suffix expression and simple calculation implementation summary
date: 2023-06-30 10:25:50
tags:

  • Expression
    categories:
  • Development knowledge and other
    cover: https://cover.png
    feature: false

1. Concept

1.1 What are prefix, infix, and postfix expressions?

  • Prefix expression : also known as Polish Notation , the operator is placed before the two operands in prefix form (for example: the prefix expression of 3 + 2 is + 3 2)
  • Infix expression : The operator is located in the middle of the operand in the form of infix (such as: 3 + 2), which is our common expression method of arithmetic and logic formulas in daily life
  • Suffix expression : also known as Reverse Polish Notation ( RPN ), the operator is placed after the two operands in the form of suffix (for example: the suffix expression of 3 + 2 is 3 2 +)

Infix expressions often need to use parentheses to enclose the operator and the corresponding operands to indicate the order of operations, such as 5 * (2 + 1) Although the priority of * is higher than +, the presence of parentheses indicates that the + operation inside the parentheses should be performed first. Suitable for human thinking structure and computing habits, but not suitable for computers

Unlike infix expressions, neither prefix nor postfix expressions need to use parentheses to identify the precedence of operators, which is suitable for computers. However, the calculation of suffix expressions is performed in the order in which operators appear from left to right (regardless of the priority between operators), which is more in line with human reading habits. Therefore, in actual computer programs, suffix expressions are basically used to store formulas, and prefix expressions are less effective. For infix expressions, we can convert them to postfix expressions first, and then evaluate them

1.2 Tree structure correspondence

In fact, prefix expressions, infix expressions, and postfix expressions are three different ways to store and calculate expressions through trees, corresponding to the pre-order traversal, in-order traversal, and post-order traversal of the tree respectively. As shown below, this is a binary tree

  • For the above tree, the preorder traversal is *+a−bcd, which corresponds to the prefix expression
  • Inorder traversal is a+b−c∗d, but this representation is ambiguous, which means that ab is a subtree, cd is a subtree, and then subtracted, so the infix expression must use parentheses to correctly express the desired result. The infix expression is: (a+(b−c))∗d, and the brackets represent the whole of a subtree
  • Postorder traversal is abc−+d∗, which is the corresponding postfix expression

2. Expression evaluation

2.1 Storing and evaluating expressions through a tree structure

The implementation idea is relatively simple. If the parameter is stored on the node, then the value of the parameter is the value of the node; if the operator is stored on the node, the left subtree and the right subtree of the node are used for corresponding operations, and the obtained result is used as the value of the node.

code abbreviation

2.2 Prefix expression parsing and evaluation

∗ + a − b c d ∗+a−bcd +abcd

Observing the rules of prefix expressions, we can find that whenever two values ​​appear in a row, there must be an operator in front of it. This is determined by the characteristics of preorder traversal (around the root, and the root is an expression). Therefore, we take three elements in turn, judge that they meet the two consecutive numerical conditions, and perform operations to obtain the value of an operator node. Repeated recursion in this way can finally find the value of the expression

code abbreviation

2.3 Postfix expression parsing and evaluation

a b c − + d ∗ abc−+d∗ abc+d

Similar to the prefix expression, it is actually the feature of post-order traversal, that is, as long as there is an operator, the first two elements must be operands (left and right roots), and then the same three elements are taken out, and the operation is judged to meet the conditions

See 3 for detailed code

2.4 Convert infix expression to postfix expression

( a + ( b − c ) ) ∗ d (a+(b−c))∗d (a+(bc))d

It is cumbersome to evaluate an infix expression directly, so we convert it to a postfix expression, and then evaluate it is convenient. The difficulty of converting an infix expression to a suffix expression is that the precedence of parentheses and operators must be considered. The steps are as follows. This conversion algorithm is not generated out of thin air, but is deduced according to the characteristics of the suffix expression

  1. Create two stacks, S1 is used to store output elements, and S2 is used to store operators. Since the operators in the expression have priority, they must be temporarily stored on the stack
  2. Read elements one by one from the top of the infix expression stack to the end of the stack
  3. If the operand is read, it is directly added to the end of the S1 stack. Because postfix expression operands always come before operators
  4. If the left parenthesis is read, it is directly pushed onto the top of the S2 stack. Because the opening parenthesis cannot be processed until the closing parenthesis
  5. If the operator is read, and the S2 stack is empty or the top element of the S2 stack is a left parenthesis, it is pushed directly to the top of the S2 stack. Because this case does not require comparison operator precedence
  6. If an operator is read, and the top of the S2 stack is also an operator, and the priority of the current operator is greater than the element at the top of the stack, then the current operator is pushed onto the top of the S2 stack. Because the operator read later may have a higher priority than the current operator, the current operator cannot be output temporarily
  7. If an operator is read, and the top of the S2 stack is also an operator, and the priority of the current operator is less than or equal to the element at the top of the stack, pop the operator at the top of the S2 stack and add it to the end of the S1 stack. Because the operator with higher precedence needs to participate in the operation first. Note that this is a recursive process, because there may already be multiple operators in S2, and their priority may be greater than or equal to the current operator. When these operators are popped up, the current operator will be pushed onto the top of the S2 stack
  8. If the right parenthesis is read, all operators above the first left parenthesis in S2 will be added to the end of the S1 stack. Because the parentheses have the highest priority, the operation is performed immediately

Example: the infix expression 2*(3+5)+7/1-4 is converted to a postfix expression

It can be converted into a tree first, and then the suffix expression is obtained by post-order traversal, and then verified with the result calculated through the above steps to determine whether it is correct. What needs to be emphasized in the conversion is that we use parentheses to indicate priority calculation

In the expression 2*(3+5)+7/1-4, we agree that * and / have higher priority than + and -, so + and - need to be calculated with parentheses. But for + and - itself, * and / have high priority, which is also a kind of priority calculation, and parentheses need to be added for priority calculation, but we agreed to calculate * and / first at the beginning, and also for convenience, so the parentheses are omitted

Including * and/or + and - at the same level. We agreed to count from left to right. In fact, the left side is counted first, which is also a priority calculation. We add parentheses to the priority calculation, so the original formula should be: ((2*(3+5))+(7/1)) -4

Emphasizing this point is mainly for the convenience of dividing the left and right subtrees when converting to a tree. The brackets are the whole of a subtree, so that the structure of the converted tree is very clear. [Left subtree operator right subtree ]

The postorder traversal is: 235+*71/+4-, which is the postfix expression

At this time, the suffix expression can be obtained through the above steps

It can be seen that the final result is also 235+*71/+4-

See 3 for detailed code

3. Realization of simple operation

Calculator class

public class Calculator {
    
    

    private static final Map<String, Integer> OPERATORS = MapUtil.builder("+", 1).put("-", 1).put("*", 2).put("/", 2)
            .put("%", 2).put("^", 3).put("(", 0).put(")", 0).build();

    private Calculator() {
    
    
    }

    public static double calculate(String equation) {
    
    
        if (!BaseUtil.isWholeSymbol(equation)) {
    
    
            throw new IllegalArgumentException("请确认括号是否完整");
        }

        Deque<String> operand = new ArrayDeque<>();
        Deque<String> operator = new ArrayDeque<>();

        for (String str : toList(equation)) {
    
    
            if (NumberUtils.isCreatable(str)) {
    
    
                operand.push(str);
                continue;
            }

            Integer opt = OPERATORS.get(str);
            if (null == opt) {
    
    
                throw new IllegalArgumentException("操作符不合法");
            }

            if (StrPool.LBRACKET.value().equals(str) || operator.isEmpty() || opt > OPERATORS.get(operator.peek())) {
    
    
                operator.push(str);
            } else if (StrPool.RBRACKET.value().equals(str)) {
    
    
                // 判断是否是右括号, 存在右括号则运算符栈必有左括号, 即运算符栈不为空
                while (!operator.isEmpty()) {
    
    
                    if (StrPool.LBRACKET.value().equals(operator.peek())) {
    
    
                        operator.pop();
                        break;
                    } else {
    
    
                        String calculate = calculate(operator.pop(), operand.pop(), operand.pop());
                        operand.push(calculate);
                    }
                }
            } else if (opt <= OPERATORS.get(operator.peek())) {
    
    
                while (!operator.isEmpty() && opt <= OPERATORS.get(operator.peek())) {
    
    
                    String calculate = calculate(operator.pop(), operand.pop(), operand.pop());
                    operand.push(calculate);
                }
                operator.push(str);
            }
        }

        while (!operator.isEmpty()) {
    
    
            String calculate = calculate(operator.pop(), operand.pop(), operand.pop());
            operand.push(calculate);
        }
        return Double.parseDouble(operand.pop());
    }


    public static List<String> toList(String str) {
    
    
        List<String> list = new ArrayList<>();
        StringBuilder builder = new StringBuilder();

        String replace = str.replaceAll("\\s*", "");
        char[] chars = replace.toCharArray();
        for (int i = 0; i < chars.length; i++) {
    
    
            boolean isMinus = '-' == chars[i] && (i == 0 || '(' == chars[i - 1]);
            if (isMinus) {
    
    
                builder.append(chars[i]);
                continue;
            }

            String val = String.valueOf(chars[i]);
            if (null != OPERATORS.get(val)) {
    
    
                if (StringUtil.INSTANCE.isNotBlank(builder)) {
    
    
                    list.add(builder.toString());
                }
                list.add(val);
                builder = new StringBuilder();
            } else {
    
    
                builder.append(chars[i]);
            }
        }

        if (StringUtil.INSTANCE.isNotBlank(builder)) {
    
    
            list.add(builder.toString());
        }
        return list;
    }

    private static String calculate(String operator, String val2, String val1) {
    
    
        double pre = Double.parseDouble(val1);
        double suf = Double.parseDouble(val2);

        switch (operator) {
    
    
            case "+":
                return pre + suf + "";
            case "-":
                return pre - suf + "";
            case "*":
                return pre * suf + "";
            case "/":
                return pre / suf + "";
            case "%":
                return pre % suf + "";
            case "^":
                return Math.pow(pre, suf) + "";
            default:
                return "0";
        }
    }
}

BaseUtil class

public class BaseUtil {
    
    

    private static final Map<Character, Character> R_SYMBOL = MapUtil.builder(')', '(').put(']', '[').put('}', '{').build();

    private static final List<Character> L_SYMBOL = ListUtil.list('(', '[', '{');

    private BaseUtil() {
    
    
    }

    public static boolean isWholeSymbol(String str) {
    
    
        Deque<Character> symbol = new ArrayDeque<>();

        for (char ch : str.toCharArray()) {
    
    
            if (R_SYMBOL.containsKey(ch)) {
    
    
                if (symbol.isEmpty() || !symbol.peek().equals(R_SYMBOL.get(ch))) {
    
    
                    return false;
                }
                symbol.pop();
            } else if (L_SYMBOL.contains(ch)) {
    
    
                symbol.push(ch);
            }
        }

        return symbol.isEmpty();
    }
}

Guess you like

Origin blog.csdn.net/ACE_U_005A/article/details/131769220