【数据结构与算法】使用栈Stack来实现后缀表达式(逆波兰表达式)求值

1. 前缀表达式的介绍

前缀表达式也叫波兰表达式。前缀表达式的运算符位于数字之前,例如(3+4)*5-6 对应的前缀表达式就是- * + 3 4 5 6。因为前缀表达式使用的不多,这里我们只要了解即可,不用关系中缀表达式如何转换成前缀表达式

通过前缀表达式求值的步骤

从右至左遍历表达式,遇到数字时,将数字压入栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算,并将结果入栈;重复上述过程直到遍历完成,最后运算得出的值即为表达式的结果

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

  1. 从右至左遍历表达式,将6、5、4、3压入栈
  2. 遇到+运算符,因此弹出3和4,计算出3+4的值,得7,再将7入栈
  3. 接下来是运算符,因此弹出7和5,计算出75=35,将35入栈
  4. 最后是-运算符,计算出35-6的值,即29,由此得出表达式最终结果

2. 中缀表达式的介绍

中缀表达式就是常见的运算表达式,如(3+4)*5-6。但对计算机来说却不好操作,有时计算的结果不正确。因此在计算结果时,往往会将中缀表达式转成后缀表达式来操作

3. 后缀表达式的介绍

后缀表达式也叫逆波兰表达式。与前缀表达式相似,只是运算符位于数字之后。例如(3+4)*5-6对应的后缀表达式就是3 4 + 5 * 6 –

4. 将中缀表达式转换成后缀表达式

后缀表达式适合计算机进行运算,但是对人却不友好,我们需要将中缀表达式转成后缀表达式

转换步骤如下

  1. 初始化一个栈stack用来储存运算符,和一个集合list用来储存中间结果
  2. 从左至右遍历中缀表达式
  3. 遇到数字时,将添加到list;
  4. 遇到括号时:
    1. 如果是左括号“(”,则直接压入stack
    2. 如果是右括号“)”,则依次弹出stack栈顶的运算符,并添加到list,直到遇到左括号为止,并将左括号弹出并丢弃
  5. 遇到运算符时,比较其与stack栈顶运算符的优先级:
    1. 如果stack不为空,且栈顶运算符不为左括号"(",且小于等于栈顶运算符的优先级,则将栈顶运算符弹出并添加到list中。再次循环处理5-1
    2. 否则直接将运算符入栈
  6. 重复步骤2至5,直到遍历表达式完成
  7. 将stack中剩余的运算符依次弹出并添加到list
  8. 遍历list的值,即为后缀表达式

中缀表达式(3+4)*5-6,转换成后缀表达式的结果为3 4 + 5 * 6 -

5. 通过后缀表达式进行求值

通过后缀表达式求值的步骤

从左至右遍历表达式,遇到数字时,将数字压入栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算,并将结果入栈;重复上述过程直到遍历完成,最后运算得出的值即为表达式的结果

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

  1. 从左至右遍历,将3和4压入栈
  2. 遇到+运算符,因此弹出4和3,计算出3+4的值,得7,再将7入栈
  3. 将5入栈
  4. 接下来是运算符,因此弹出5和7,计算出75=35,将35入栈
  5. 将6入栈
  6. 最后是-运算符,计算出35-6的值,即29,由此得出表达式最终结果

6. 逆波兰表达式的代码实现

需求:输入一个中缀表达式,支持小括号和多位数正数,运算符支持+、-、*、/四种。先使用栈Stack转换成逆波兰表达式,最后使用栈Stack对逆波兰表达式进行计算,得出表达式结果
输入一个逆波兰表达式(后缀表达式),使用栈(Stack), 计算其结果

实现如下

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

public class ReversePolishNotation {

    public static void main(String[] args) {
        
        String infixExpression = "(3+4)*5-6";

        // 中缀表达式转成列表
        List<String> infixList = infixExpression2List(infixExpression);
        System.out.println(infixList);

        // 中缀列表,转换成后缀列表
        List<String> suffixList = infixList2SuffixList(infixList);
        System.out.println(suffixList);

        int result = reversePolishCalculate(suffixList);
        System.out.printf("表达式:%s的计算结果为:%d\n", infixExpression, result);
    }


    // 将中缀表达式转换成list
    public static List<String> infixExpression2List(String infixExpression) {
        List<String> infixList = new ArrayList<String>();
        int index = 0;
        char tmpChar;
        String concatNumber = ""; // 用于拼接多位数字字符

        while (index < infixExpression.length()) {
            tmpChar = infixExpression.charAt(index);

            // 如果是一个非数字,则直接添加到list,然后进行下一轮遍历
            if (tmpChar < 48 || tmpChar > 57) {
                infixList.add("" + tmpChar);
            } else {
                // 如果是一个数字,则拼接到数字字符串
                concatNumber += tmpChar;

                // 如果当前字符是最后一个字符,或当前字符的下一个字符是非数字
                // 则将数字字符串添加到list, 然后将数字字符串变为""
                if (index == (infixExpression.length() - 1) ||
                        infixExpression.charAt(index + 1) < 48 ||
                        infixExpression.charAt(index + 1) > 57) {
                    infixList.add(concatNumber);
                    concatNumber = "";
                }
            }

            index++;
        }

        return infixList;
    }


    // 将中缀表达式列表,转换成后缀表达式列表
    public static List<String> infixList2SuffixList(List<String> infixList) {
        // stack用来储存运算符
        Stack<String> stack = new Stack<String>();
        // suffixList用来储存中间结果
        List<String> suffixList = new ArrayList<String>();

        for (String infix : infixList) {
            // 如果是一个数,添加到suffixList
            if (infix.matches("\\d+")) {
                suffixList.add(infix);
            } else if (infix.equals("(")) {
                // 如果是左括号“(”,则直接压入stack
                stack.push(infix);
            } else if (infix.equals(")")) {
                // 如果是右括号“)”,则依次弹出stack栈顶的运算符,并添加到suffixList,直到遇到左括号为止,并将左括号弹出并丢弃
                while (!stack.peek().equals("(")) {
                    suffixList.add(stack.pop());
                }
                stack.pop();
            } else {
                // 运算符处理

                // 如果stack不为空,且栈顶运算符不为左括号"(",且小于等于栈顶运算符的优先级,则将栈顶运算符弹出并添加到suffixList中。再次循环处理5-1
                // 否则直接将运算符入栈
                while (!stack.isEmpty() && stack.peek() != "(" && getOperationPriority(infix) <= getOperationPriority(stack.peek())) {
                    suffixList.add(stack.pop());
                }
                stack.push(infix);
            }
        }

        // 将stack中剩余的运算符依次弹出并添加到suffixList
        while (!stack.isEmpty()) {
            suffixList.add(stack.pop());
        }

        return suffixList;
    }


    // 传入后缀列表,对逆波兰表达式进行计算
    public static int reversePolishCalculate(List<String> suffixList) {
        Stack<String> stack = new Stack<String>();
        int result;

        for (String suffix : suffixList) {
            // 如果是数字,则直接入栈
            if (suffix.matches("\\d+")) {
                stack.push(suffix);
            } else {
                // 如果是运算符,则从栈中弹出两个数字进行计算
                // 然后将结果入栈
                int number1 = Integer.parseInt(stack.pop());
                int number2 = Integer.parseInt(stack.pop());

                if (suffix.equals("+")) {
                    result = number1 + number2;
                } else if (suffix.equals("-")) {
                    result = number2 - number1;
                } else if (suffix.equals("*")) {
                    result = number1 * number2;
                } else if (suffix.equals("/")) {
                    result = number2 / number1;
                } else {
                    throw new RuntimeException("不能处理运算符");
                }
                // 将result入栈
                stack.push("" + result);
            }

        }

        // 最后留在stack中的数据是运算结果
        return Integer.parseInt(stack.pop());
    }


    // 获取运算符的优先级
    public static int getOperationPriority(String operation) {
        int operationPriority = 0;
        switch (operation) {
            case "+":
                operationPriority = 1;
                break;
            case "-":
                operationPriority = 1;
                break;
            case "*":
                operationPriority = 2;
                break;
            case "/":
                operationPriority = 2;
                break;
            default:
                break;
        }
        return operationPriority;
    }
}

运行程序,结果如下:

[(, 3, +, 4, ), *, 5, -, 6]
[3, 4, +, 5, *, 6, -]
表达式:(3+4)*5-6的计算结果为:29

猜你喜欢

转载自blog.csdn.net/yy8623977/article/details/126636686