逆波兰表达式

来源:互联网 发布:mysql alias 编辑:程序博客网 时间:2024/05/22 01:43

问题来由:

读入一个字符串形式的四则运算表达式,输出对应的计算结果。如读入的是“6 * ( 5 + ( 2 + 3) * 8 + 3)”,那么解析后的输出结果应为288。

思路:

  1. 一般的计算过程是这样的,首先计算优先级最高的小括号里面的内容,即“( 5 + ( 2 + 3) * 8 + 3)”,
  2. 将“2 + 3”的计算结果并存为A,接着用计算“A*8”,并存为B
  3. 计算“5+B+3”,结果存为C
  4. 最后计算“6*C”,计算完毕

我们可以将这种操作顺序书写如下:

6 5 2 3 + 8 * + 3 + *

这个记法就是逆波兰(reverse Polish)表达式,其求值过程恰好就是上面所描述的过程。逆波兰表达式又叫做后缀(postfix)表达式。在通常的表达式中,运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。波兰逻辑学家 J.Lukasiewicz于1929年提出了另一种表示表达式的方法。按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。

后缀表达式的计算

计算后缀表达式最简单的方法就是使用一个栈:

  • 当读到一个数字时就将它压入栈中
  • 读到一个运算符时,就从栈中弹出两个数字,并将该运算符作用于这两个数字,然后将计算结果再压入栈中

下面演示下“6 5 2 3 + 8 * + 3 + *”的处理过程

  1. 首先读到 6 5 2 3 四个数字,没啥说的,直接打入栈中(6 5 2 3)
  2. 读到运算符“+”,从栈中弹出两个数字“3”、“2”,计算 3+2 = 5,将计算结果压入栈中;(6 5 5)
  3. 读到8,压入栈(6 5 5 8)
  4. 读到“*”,从栈中弹出两个数字“8”、“5”,计算 8 * 5 = 40,将计算结果压入栈中;(6 5 40)
  5. 读到“+”,从栈中弹出两个数字“40”、“5”,计算 40 + 5 = 45,将计算结果压入栈中;(6 45)
  6. 读到3,压入栈(6 45 3)
  7. 读到“+”,从栈中弹出两个数字“3”、“45”,计算 3 + 45 = 48,将计算结果压入栈中;(6 48)
  8. 读到“*”,从栈中弹出两个数字“48”、“6”,计算 48 * 6 = 288,计算完毕!!!

image

计算一个后缀表达式话费的时间是O(N),该算法的计算非常简单,同时不需要知道任何的计算优先级。

那么现在的问题就是如何将一个正常的表达式转换为后缀表达式???


其实真正的难点在于如何将一个正常的字符串表达式(中缀表达式)变成一个后缀表达式。如将6 * ( 5 + ( 2 + 3) * 8 + 3)变为6 5 2 3 + 8 * + 3 + *

逆波兰表达式,它的语法规定,表达式必须以逆波兰表达式的方式给出。逆波兰表达式又叫做后缀表达式。这个知识点在数据结构和编译原理这两门课程中都有介绍,下面是一些例子:

正常的中缀表示逆波兰表达式a+ba,b,+a+(b-c)a,b,c,-,+a+(b-c)*da,b,c,-,d,*,+a+d*(b-c)a,d,b,c,-,*,+

首先约定表达式中运算符的优先级,从大到小依次为:()、* 和 /、+ 和 -。暂时只考虑四则运算。

顺序读取字符串表达式,规则:

  1. 读到的是操作数,直接输出;
  2. 读到的是操作符(+-*/)(记为read),将其与栈顶的操作符(记为top)进行优先级比较:read>top,read入栈,继续读下一个;read≤top,top出栈,并输出到list中,read继续和新的top比较;top为空,read直接入栈;若top是“(”,read直接入栈,因为“(”优先级最高;
  3. 括号的处理:读到左括号“(”,直接将其压入栈中,并且除非遇到右括号“)”,“(”是不会弹出的;读到右括号“)”,将“(”之上的元素全部依次输出,并弹出“(”但不输出;

准备:一个栈stack暂存运算符,一个list存放输出,仍以6 * ( 5 + ( 2 + 3) * 8 + 3)为例。

  1. 读到“6”,直接输出【list:6;stack:】
  2. 读到“*”,与栈顶top比较,top为空,“*”入栈【list:6;stack:*】
  3. 读到“(”,直接入栈【list:6;stack:*,(】
  4. 读到“5”,直接输出【list:6,5;stack:*,(】
  5. 读到“+”,与栈顶top比较,“+”<“(”,入栈【list:6,5;stack:*,(,+】
  6. 读到“(”,直接入栈【list:6,5;stack:*,(,+,(】
  7. 读到“2”,直接输出【list:6,5,2,;stack:*,(,+,(】
  8. 读到“+”,与栈顶“(”比较,“+”<“(”,入栈【list:6,5,2,;stack:*,(,+,(,+】
  9. 读到“3”,直接输出【list:6,5,2,3,;stack:*,(,+,(,+】
  10. 读到“)”,输出“(”之上的元素【list:6,5,2,3,+;stack:*,(,+,】
  11. 读到“*”,与栈顶的“+”比较, “*”>“+”,“*”入栈【list:6,5,2,3,+;stack:*,(,+,*,】
  12. 读到“8”,直接输出【list:6,5,2,3,+,8,;stack:*,(,+,*,】
  13. 读到“+”,与栈顶的“*”比较,“+”<“*”,“*”出栈并输出【list:6,5,2,3,+,8,*;stack:*,(,+,】;
  14. “+”,与栈顶的“+”比较,“+”=“+”,“+”出栈并输出【list:6,5,2,3,+,8,*,+;stack:*,(,】;
  15. “+”,与栈顶的“(”比较,“+”<“(”,“+”入栈【list:6,5,2,3,+,8,*,+;stack:*,(,+】;
  16. 读到“3”,直接输出,【list:6,5,2,3,+,8,*,+,3;stack:*,(,+】
  17. 读到“)”,输出“(”之上的元素,【list:6,5,2,3,+,8,*,+,3,+;stack:*,】
  18. 输出栈中所有剩下的,【list:6,5,2,3,+,8,*,+,3,+,*;stack:,】
  19. 最终的后缀表达式就是:6 5 2 3 + 8 * + 3 + *

image

 

至此,中缀表达式到后缀表达式的任务已经全部完成!

但是需要注意的是,本文中只是做了当运算数据为个位数的处理,当为多位数时该怎么做呢?

另外当遇到“/”处时,要对被除数进行非0判断

另外还可以考虑含有负数的运算,含有小数的以及幂运算

 

那都是数据如何处理的问题了,整体思路应该都差不多!

逛博客的时候发现了这个有意思的东西,很久之前碰到过这个问题,有认真的想过,也只是想过,也没有具体做过,今天碰到神人,把这个具体的写出来, 遂转来之,仅供参考学习之用,原文地址在此
0 0
原创粉丝点击