栈的应用——运算表达式求值

我们来看一个栈比较常见的应用:数学表达式的求值。


对于任意一个四则运算表达式,比如“9+(5-1)*3+8/2”。我们采用“从左到右,先乘除,后加减,先括号内,后括号外”的原则,口算就可以得出结果,但是计算机是如果算出它的值呢?


后缀(逆波兰)表达式

逆波兰表示法(RPN),是一种由波兰数学家Jan Łukasiewicz 1920年引入的数学表达式,在RPN中,所有操作符置于操作数的后面,因此也被称为后缀表达式(Postfix Notation)。后缀表达式不需要括号来标识操作符的优先级。


而我们生活中使用的表达式方法是中缀表达式(Infix Notation),即操作符穿插在数字中间。

下面的表格展示了中缀表达式和后缀表达式的对比:

栈的应用——运算表达式求值_第1张图片

而对于之前的例子“9+(5-1)*3+8/2”,它的后缀表达式为“951-3*+82/+”。


下面我们来看如何将中缀表达式转换为后缀表达式:

1.创建一个操作栈来存放操作符。

2.将输入的字符串转换为List.

3.遍历List

  • 如果是数字就输出,成为后缀表达式的一部分;
  • 如果是左括号,将其入栈;
  • 如果是右括号,将操作栈中所有左括号之上的符号依次出栈,输出到后缀表达式;
  • 如果是操作符,判断其与栈顶符号的优先级,如果优先级低于栈顶符号(乘除优先加减),则栈顶元素依次出栈并输出,并将当前符号进栈。

4.当表达式遍历完成,将操作栈中剩余的符号依次出栈并输出。


下面以“9+(5-1)*3+8/2”为例子做演示:



算法代码:

public static List getSuffixCharList(String s) {
        Stack stack = new Stack();
        List list = new ArrayList<>();

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (Character.isDigit(c)) {
                list.add(c);
            } else {
                if (!stack.empty()) {
                    while (!stack.empty() && priority(c, stack.peek())) { //判断符号优先级
                        if (judgeAngel(c, stack.peek())) { //右括号匹配
                            stack.pop();
                            break;
                        }
                        list.add(stack.pop());
                    }
                    if (c != CLOSE_ANGEL) {
                        stack.push(c);
                    }
                } else {
                    stack.push(c);
                }
            }
        }
        while (!stack.isEmpty()) {
            list.add(stack.pop());
        }
        return list;
    }

后缀表达式求值

好了,现在我们成功把中缀表达式转换为后缀表达式了,接下来看怎么通过后缀表达式求值:

1.创建一个操作栈来存放数字;

2.将输入的字符串转换为List;

3.遍历List

  • 如果是数字就进栈
  • 如果是符号,就将处于栈顶两个数字出栈,进行运算,运算结构进栈。(注意,对于减法和除法,第二个出栈的数字为被减/除数)

4.当表达式遍历完成,栈中的数字即为最终的计算结果。


同样我们以“9+(5-1)*3+8/2”为例子做演示:



算法代码:

public static int getResult(List characters) {
        Stack stack = new Stack();
        for (int i = 0; i < characters.size(); i++) {
            char c = characters.get(i);
            if (Character.isDigit(c)) {
                stack.push(Integer.parseInt(String.valueOf(c)));
            } else {
                int x = stack.pop();
                int y = stack.pop();
                stack.push(operator(y, x, c));
            }
        }

        return stack.pop();
    }

好了,通过栈来求数学表达式的值已经讲完了,总结一下其实就是两个步骤:

1.将中缀表达式转化为后缀表达式(栈用来进出运算的符号)

2.将后缀表达式进行运算得出结构(栈用来进出运算的数字)

两个步骤都是利用了栈的后进先出的特性,其实理解了栈的这个特性,也就很好理解这个算法了。


————————————————————————————————————

文末推荐个人开发的app,Google Play : 数据结构与算法教程 (需要科学上网)

    

提供了丰富的动画演示、模拟场景来帮助你更好的理解抽象的数据结构和复杂的算法。(文中的动图都是从app中截取的片段)


你可能感兴趣的:(数据结构与算法)