2010.05.13 自学数据结构 之 Reverse Polish Notation

来源:互联网 发布:js insertafter 编辑:程序博客网 时间:2024/05/16 04:27

最近把c primerplus看的差不多了。在看到关于栈的部分时,我想到以前见过的逆波兰表达,正好可以用来练习对栈的操作。
所谓Reverse PolishNotation(RPN),在我的理解,就是将二元运算符置于运算元素之后,从而可以顺序执行所有操作并得出结果。运算过程中,所有元素都需要存放在一个临时的栈中,每当遇到一个2元操作符,则弹出栈顶的两个元素参与计算,将结果保存回栈中。
比如,一个普通的整数运算表达式:(5+3*2)/(7 - (8/2+1)),用RPN表示为:
5 3 2 * + 7 8 2 / 1 + - /
操作时,需要一个栈。上式的操作步骤为:
5入栈
3入栈
2入栈
(*,弹出栈顶两个元素)2出栈,3出栈,(计算得3*2=6)6入栈。
(+,弹出栈顶两个元素)6出栈,5出栈,(计算得5+6=11)11入栈
7入栈
8入栈
2入栈
(/,弹出两元素)2出栈,8出栈,(计算得8/2=4)4入栈
1入栈
(+,弹出两元素)1出栈,4出栈,(计算得4+1=5)5入栈
(-,弹出两元素)5出栈,7出栈,(计算得7-5=2)2入栈
(/,弹出两元素)2出栈,11出栈,(计算的11/2=5)5入栈
所有操作结束,弹出栈中结果即最终结果:5。
RPN的好处在于,不需要考虑加减乘除幂模以及括号(没有括号了)的优先级关系。而且相对于中缀表达式(类似于5*2,3+4),运算时所占的存储空间也小。实现起来也很简洁方便,从wiki上得知,早期的计算器都采用这种模式。
那么就来看看如何把我们熟悉的中缀表达式转化为RPN,毕竟写算数题都是写137-(46*22+578)之类的。
首先输入的类型。
有整数,四则运算(可以包括模运算符,幂运算符),以及括号。
1.整数。可以直接输出。因为运算符可能因为优先级不同使得输入输出的顺序不同,但数总是按照输入的顺序输出,而不会改变位置,没听说哪个数字有优先权……
2. 运算符。不能直接输出。因为符号间具有优先级差异,需要比较后才能输出。用一个栈存储运算符。
3. 括号。括号提升了其内部运算的优先级。而从一对括号的内部来看,转换法则是与1,2相同的。
今天不早了,下次再把算法归纳一下。

--------------------------------------------------

周末了,把之前的补完一下。

写这个主要是为了巩固自学的数据结构知识。栈和链表。
用栈临时存储运算符。用链表来存储整数和运算符,结构体里包含value和flag,flag用来标记是整数还是运算符。

取整数的方法:
1.遇到整数字符,就顺序存到一个临时数组里,直到非整数字符出现,然后用atoi函数把字符组转换一下。
2.将该数append到链表尾部。
3.取下一个字符。

取运算符的方法:
运算符不能直接append到链表。

遇到运算符,
1.先到栈(假设为S)里取元素,
1).如果栈是空的,就把该运算符(假设为A)存进去。Push(A,S)
2).如果栈不空,Pop(S),将取出来的运算符(假设为B)的优先级与A比较。
   1>当B优先级小于A时,将B压入栈里,并将A也存入栈里。
   2>当B优先级高于A或者等于A(相等其实也表示高,因为B在A前面)时,将B添加到链表尾部。此时继续从栈中取元素,并与A比较,直到所得元素优先级小于A或者栈空了(或者遇到左括号了)。然后将A压入栈里。
2.取下一个字符。

去除括号的方法:
左括号:
将左括号直接压入栈中。
右括号:
1.遇到右括号,从栈中取运算符,添加到链表尾部。
2.循环步骤1直到取出左括号。
3.取下一个字符。
原因也很简单,括号使其内部的所有运算的优先级都比其外部高。而内部运算符之间关系遵循取运算符的方法。

最后:
字符取完了,自然要把栈里所有的运算符依次取出添加到链表尾部。
还有一些处理空格字符,错误判断的问题就不说了,比如(遇到右括号,却从栈里找不到左括号之类……)。
那么,把一个中缀表达式转化为RPN的工作就完成了。

其他:
一元运算符(+/-),幂运算符(^)也可以补充上去,设置成相应的优先级就可以了。
我现在想想这个转化的过程,如果把链表替换成另一个栈,就可以直接做运算了。
那么最后得数自然也就是计算结果了。