简单易懂的中缀转后缀表达式算法详解(Java版)逆波兰表达式

中缀表达式转为后缀表达式 是实现逆波兰计算器的核心算法,其使用 栈(stack) 这一数据结构实现。本文转化算法(java版本)支持 整数和括号 的计算表达式转换。


1 前、中、后缀表达式

前缀表达式、中缀表达式、后缀表达式都是四则运算的表达方式,用以四则运算表达式求值。

  • 中缀表达式:就是平常我们写的表达式。如1-(2+3) 和 1+((2+3)*4)-5。
  • 前缀表达式(波兰式):通俗来说就是运算符写在前面,操作数写在后面。如 1+((2+3)*4)-5 对应的前缀表达式为 - + 1 * + 2 3 4 5 中缀转前缀也有一套算法。(前缀表达式介绍)
  • 后缀表示式(逆波兰式):运算符位于操作数之后,本文重点介绍转化算法。

2 中缀转后缀表达式算法

2.1 转化算法伪代码(由波兰数学家提出)

  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中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式。

2.2 具体示例 1+((2+3)×4)-5 转换步骤

扫描到的元素 s2 (栈底-栈顶) s1 (栈底->栈顶) 说明
1 1 数字,直接入栈
+ 1 + s1为空,运算符直接入栈
( 1 + ( 左括号,直接入栈
( 1 + ( ( 左括号,直接入栈
2 1 2 + ( ( 数字,直接入栈
+ 1 2 + ( ( + s1栈顶为左括号,运算符直接入栈
3 1 2 3 + ( ( + 数字,直接入栈
) 1 2 3 + + ( 右括号,弹出运算符直至遇到左括号
× 1 2 3 + + ( × s1栈顶为左括号,运算符直接入栈
4 1 2 3 + 4 + ( × 数字,直接入栈
) 1 2 3 + 4 × + 右括号,弹出运算符直至遇到左括号
- 1 2 3 + 4 × + - -与+优先级相同,因此弹出+,再压入-
5 1 2 3 + 4 × + 5 - 数字
到达最右端 1 2 3 + 4 × + 5 - s1中剩余的运算符

因此结果为 :1 2 3 + 4 × + 5 -

2.3 Java算法代码

package stack;

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

/**
 * created by hbl on 2020/3/16.
 */
public class PolandNotation {
    public static void main(String[] args) {
        String expression = "1+((2+3)*4)-5";
        List<String> infixList = string2List(expression);
        List<String> suffixList = infixList2SuffixList(infixList);
        int res = calculate(suffixList);
        System.out.printf("表达式 %s=%d", expression, res);
    }

    /**
     * 将中缀表达式转换为后缀表达式.
     * 特别注意:因为存储中间结果的栈在算法操作过程中没有使用pop操作,并且最后还要逆序输出,
     * 我将其改为ArrayList进行存储,这样也不用逆序输出了。
     *
     * @param infixList 中缀表达式组成的List
     * @return 后缀表达式的list
     */
    private static List<String> infixList2SuffixList(List<String> infixList) {
        Stack<String> stack = new Stack<>();  //符号栈s1
        ArrayList<String> list = new ArrayList<>(); // 中间结果栈s2
        for (String item : infixList) {
            if (item.matches("\\d+")) {
                list.add(item);
            } else if (item.equals("(")) {
                stack.push(item);
            } else if (item.equals(")")) {
                while (!stack.peek().equals("(")) {
                    list.add(stack.pop());
                }
                stack.pop();
            } else {
                if (stack.size() != 0 && getValue(item) <= getValue(stack.peek())) {
                    while (stack.size() != 0 && getValue(item) <= getValue(stack.peek())) {
                        list.add(stack.pop());
                    }
                }
                stack.push(item);
            }
        }
        while (stack.size() != 0) {
            list.add(stack.pop());
        }
        return list;
    }

    private static int getValue(String item) {
        if (item.equals("+") || item.equals("-")) {
            return 1;
        } else if (item.equals("*") || item.equals("/")) {
            return 2;
        } else {
            return 0;  //算法中如果(主算法中括号则会直接放进去,这里是查看栈中的优先级,
            			//而栈中'('是没有优先级的,因为栈顶是(则直接放进去操作符。
        }
    }

    private static List<String> string2List(String expression) {
        ArrayList<String> list = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (c < 48 || c > 57) {
                list.add("" + c);
            } else {
                sb.append(c);
                if (i == expression.length() - 1) {
                    list.add(sb.toString());
                } else {
                    char cNext = expression.charAt(i + 1);
                    if (cNext < 48 || cNext > 57) {
                        list.add(sb.toString());
                        sb.setLength(0);
                    }
                }
            }
        }
        return list;
    }


    private static int calculate(List<String> list) {
        Stack<Integer> stack = new Stack<>();
        for (String item : list) {
            if (item.matches("\\d+")) {
                stack.push(Integer.valueOf(item));
            } else {
                int num1 = stack.pop();
                int num2 = stack.pop();
                int res = getRes(num1, num2, item);
                stack.push(res);
            }
        }
        return stack.pop();
    }

    private static int getRes(int num1, int num2, String item) {
        int res = 0;
        switch (item) {
            case "+":
                res = num1 + num2;
                break;
            case "-":
                res = num2 - num1;
                break;
            case "*":
                res = num1 * num2;
                break;
            case "/":
                res = num2 / num1;
                break;
        }
        return res;
    }

}

如发现错误或不足,请评论告知。

如果觉得有用,请点个吧!蟹蟹~~

发布了4 篇原创文章 · 获赞 8 · 访问量 486

猜你喜欢

转载自blog.csdn.net/DoSmall/article/details/104922274
今日推荐