数据结构の学习(五):将中缀表达式转换为后缀表达式及求值

(1)给表达式加括号(2)创建解析树表达式(3)树的后序遍历,生成后缀表达式(4)后缀表达式求值。经过前面几个步骤的洗礼,相信你内心充满了喜悦和兴趣。现在我们只需要将后缀表达式计算输出即可,然而可怕的是:-$

不借助树我们也能轻松实现上述过程。这种方法便是逆波兰表示法(Reverse Polish Network)。我们将着重介绍这一神奇的方法。


不幸的是,笔者的博客将不会有完整代码(示范代码除外),所有代码均已上传到码云上。如果时间和精力允许的话,强烈建议你根据思路,手动写一遍,相信你会感觉到全身毛孔舒张而不是想砸电脑的快感。

好了,按照惯例,我们先介绍一些术语。


栈: 存储数据的结构,讲究“先进后出”,即最先进栈的数据,最后出栈;有顺序存储和链式存储两种。
队列:存储数据的结构,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

我们建立一个堆栈Stack()(用于存放运算符)和一个Queue()(用于存放后缀表达式)。思路是这样的,我们依次遍历中缀表达式(可以不带括号),记为i。

(1)遇到操作数直接输出到队列中。

(2)遇到'('则直接压入栈,因为它的优先级最小。

(3)遇到')',则开始出栈,将出栈的元素依次加入到队列中,直到遇到'('为止。

(4)遇到运算符,则比较复杂了。还好,你是一个心细如丝的人,你不会放过一个可能导致程序漏洞的bug。我们希望队列前面是最先被计算(优先级最高)的式子,比如1+(2+3)*4-5,我们得到后缀表达式为123+4*+5-。因此我们必须把i和栈顶元素作比较:如果i优先级大于栈顶元素,则可以直接入栈(如果栈是空的,我们得往栈压入一个运算符,不用比较);否则,将Stack出栈,输出到队列中,直到i优先级大于栈顶或者栈空为止。

(5)完成遍历后,若栈内还有元素,将它们依次输出到队列即可。但需要注意的是,我们此时将队列输出是一个逆序的后缀表达式。

如果你觉得理解起来比较吃力,我将画出这个过程,(人们总是喜欢配图的算法博客:-^),以1+(2+3)*4-5为例:

数据结构の学习(五):将中缀表达式转换为后缀表达式及求值_第1张图片

相信结合上述图,你已经熟悉了这种RPN。下面讲解如何计算后缀表达式。

从左到右遍历后缀表达式,我们准备一个数字栈s_num

(1)若遇到数字则压入s_num。

(2)遇到 运算符则,弹出s_num最上面两个元素,与操作符一起运算。将计算的结果重新压入栈。这一步很容易被未来的软件工程狮忽视。如此便计算出后缀表达式的结果。当然,遇到原理性问题比如1/0和log0和0^0,你的程序应该也能抛出相应的异常。

 

实现上述算法的好处是,你可以将二叉树得到的结果和RPN法得到的结果进行对比,如果两者不一样,一定是你的树出了问题。要么是给表达式加括号出了问题,要么是表达式解析树建立出了问题。笔者检测出无数bug也是这个原因。

下面是笔者一些测试数据,你在我的码云中也能看到:

编号

测试数据

TREE

TREELESS(RPN)

1

1

1.0

1.0

2

1+1

2.0

2.0

3

1+1*3

4.0

4.0

4

5/(1+5/6*3-3%2)*0.5+2/3+(2+3)/3

3.333333333333333

3.333333333333333

5

2^5

32.0

32.0

6

2%5^4

2.0

2.0

7

(((3*(4+(66.6/6)))+(7*(9+(((4+2)%2)/2))))+(((3/2)-1)/0.5))/(5^2)

4.372

4.372

8

4.2/(-3.5*+2+2^-2.2/4*5%6)+3-(-4.5)

6.875738797245451

6.875738797245452

9

1/0

RAISE ERROR

RAISE ERROR

10

1/(2-1)

1

1

11

(5/6+1)/0.5^5+2

60.66666666666667

60.66666666666667

12

(5/0.5^5%2*3+1)/0.5^5%2*3+1

1.0

1.0

参考资料:https://www.cnblogs.com/icodes8238/p/12243275.html 

希望笔者的博客能对你有帮助,如果有问题,欢迎留言,或者email [email protected]! 当然你给我Gitee ,Fork&Star一下,笔者也是不胜感激的!

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