表达式求值--栈(c++/java实现)

本文介绍了如何使用栈实现中缀表达式转化为后缀表达式并进行求值,详细讲解了思路和算法,包括中缀表达式到后缀表达式的转换规则,以及后缀表达式的求值过程。文中提供了具体的代码实现,并通过实例解释了表达式求值过程中的优先级处理。
摘要由CSDN通过智能技术生成

GitHub:https://github.com/dreamhuan/expression-evaluation(C++版本,Java版本在下面那个链接里面)

寒假无聊想起来以前学Java时留的一个坑(填完坑版本:GitHub:https://github.com/dreamhuan/draw-mathematics-function)写了一个画函数的东西,然后卡在表达式求值那一块上,当时挖了个坑,先用简单的表达式代替,所以只能画sin(x),x^2这样的图像(if(expression.equals("sin(x)")。。。)然后正好学了数据结构,就稍微学习了下表达式求值

因为我原本就是为了写那个画数学函数的程序的,所以我的表达式求值有+ - * / % ^ F(x),其中^是次方,对应内部就是pow函数,F(x)是常用一元函数,比如sin(x),ln(x)之类,还支持未知数x,后面再输入x,具体为啥要这么做可以看那个Java的函数绘图项目(第二个GitHub)在这里只是输入后遇到x替换成后面的值就好了。
优先级:
1 + -
2 * / %
3 ^
4 F(x)
至于为啥没有阶乘(!)因为数字默认是double,没有阶乘运算,与之对应的γ函数的话Java没有相应的库函数,于是就放弃了。然后一般画函数里面也很少出现阶乘的吧。。。

思路

参考了挺多内容的,启发最大的是这两篇:
http://blog.csdn.net/thecrazyboy/article/details/1523653
http://www.2cto.com/kf/201208/145581.html
然后引用出这两篇分别对我的启发点(修改了一点格式)

中缀表达式翻译成后缀表达式的方法如下:
1. 从右向左依次取得数据ch。
2. 如果ch是操作数,直接输出。
3. 如果ch是运算符(含左右括号),则:
 a:如果ch = ‘(‘,放入堆栈。
 b:如果ch = ‘)’,依次输出堆栈中的运算符,直到遇到’(‘为止。
 c:如果ch不是’)’或者’(‘,那么就和堆栈顶点位置的运算符top做优先级比较。
  1:如果ch优先级比top高,那么将ch放入堆栈。
  2:如果ch优先级低于或者等于top,那么输出top,然后将ch放入堆栈。
4. 如果表达式已经读取完成,而堆栈中还有运算符时,依次由顶端输出。
如果我们有表达式(A-B)*C+D-E/F,要翻译成后缀表达式,并且把后缀表达式存储在一个名叫output的字符串中,可以用下面的步骤
(1)读取’(‘,压入堆栈,output为空
(2)读取A,是运算数,直接输出到output字符串,output = A
(3)读取’-‘,此时栈里面只有一个’(‘,因此将’-‘压入栈,output = A
(4)读取B,是运算数,直接输出到output字符串,output = AB
(5)读取’)’,这时候依次输出栈里面的运算符’-‘,然后就是’(‘,直接弹出,output = AB-
(6)读取’*’,是运算符,由于此时栈为空,因此直接压入栈,output = AB-
(7)读取C,是运算数,直接输出到output字符串,output = AB-C
(8)读取’+’,是运算符,它的优先级比’‘低,那么弹出’‘,压入’+”,output = AB-C*
(9)读取D,是运算数,直接输出到output字符串,output = AB-C*D
(10)读取’-‘,是运算符,和’+’的优先级一样,因此弹出’+’,然后压入’-‘,output = AB-C*D+
(11)读取E,是运算数,直接输出到output字符串,output = AB-C*D+E
(12)读取’/’,是运算符,比’-‘的优先级高,因此压入栈,output = AB-C*D+E
(13)读取F,是运算数,直接输出到output字符串,output = AB-C*D+EF
(14)原始字符串已经读取完毕,将栈里面剩余的运算符依次弹出,output = AB-C*D+EF/-

后缀表达式求值
对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,而且操作符的优先级也不再起作用了。您可以用如下算法对后缀表达式求值:
初始化一个空堆栈
从左到右读入后缀表达式
如果字符是一个操作数,把它压入堆栈。
如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。
到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。

引用完毕,这两段话提供了很清晰的思路,还有一个实例。我简单谈谈我的理解。表达式求值的最大问题在于不同运算符的优先级以及小括号带来的优先级变化导致一个式子的求值不是从左到右的,是按优先级高低来的,同级下才从左到右。

举个例子:(2-1)*3+4-6/5 (上文(A-B)*C+D-E/F的具体化)
1:2-1=1,式子变成1*3+4-6/5
2:1*3=3,式子变成3+4-6/5
3:6/5=1.2(数据默认是double类型),式子变成3+4-1.2
4: 3+4=7,式子变成7-1.2
5:7-1.2=5.8,结果为5.8
可以看到第3步是先算了后面的除法,而不能单纯从左到右算加法。这就是优先级带来的麻烦。(这里有个小问题,数学上是先乘除后加减,然而计算机里应该先算前面的加法最后变成7-6/5这里就必须先算除法了,后面都是按照上述步骤写的,但是不影响整体的思路。这是我的一个疏忽,抱歉。)

但是我们真正做运算时是有一个明确的顺序的,为了方便体现这个顺序,所以才有了所谓的前缀中缀后缀表达式之分。(三个的区别仅仅在于运算符的位置,操作数A、B,运算符op,以op的位置命名的。A op B就是中缀,也就是我们熟悉的式子。前缀是 op A B,后缀是A B op,这两种不是写给人看的,只是方便计算机运算而存在的。具体点中缀:1 + 1,前缀:+ 1 1,后缀:1 1 +)提到前缀中缀后缀那么不得不提一提表达式树

对于(2-1)*3+4-6/5 这个例子,我们按照上面的执行顺序构建表达式树。
1:2-1 步骤1

2:(2-1)*3 步骤2

3:6/5 步骤3

4:(2-1)*3+4 步骤4

5:(2-1)*3+4-6/5

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值