栈解决中缀表达式转后缀表达式_中缀表达式转后缀表达式后计算结果

前段时间自学java想做个画函数图像的app才发现:java里没有eval函数!

无奈之下只能学习下计算表达式的过程:后缀表达式。

1.人脑计算(中缀表达式)

我们计算一个式子是从左往右看,遇到数先保留在脑子里,遇到运算符也先存在大脑,接着遇到下一个数,还得看看这个数后面有没有更高等级的运算符,遇到左括号得把之前的东西都先放着,再往后看,直到遇到右括号再算括号里的东西,然后再按照运算优先级,以3+4*(2+3)为例:

9c7d6ee58c352f6d60ac1cf90a062f0f.png

对于一个表达式,人脑中大概就是这样的过程,这样的式子也叫中缀表达式。

2.后缀表达式

但是计算机可不会这么机智的跳过不能优先计算的式子。计算机的内存结构是栈式结构,他只能执行先进后出的操作。通俗来说,计算机只会无脑从左往右遍历,上面的例子比较简单,需要跳过的运算不太多,用简单分for循环和if判断也可以实现,但是如果式子复杂到上百个运算符,恐怕会相当困难,所以我们才需要特殊的办法。

首先我列出上面式子的后缀表达式和最后的计算以便更好理解:

后缀表达式:3423+*+

计算方式:从左到右遍历表达式的每个符号,并准备一个储存空间(栈)。如果是数字,直接放到栈里(压栈),如果遇到运算符,则要将运算符前的两个数字取出并做运算符对应的运算再把这个数压栈,运算符和刚才遇到的两个数就被抛弃了。

栈看成一个柱状的容器,符号就是个大圆饼一个一个符号往容器里放:

b4feb93e112426aac256a053ebc48b9b.png

运算过程如下:

30458050254e871e5fba1f30d554d92f.png

可以看到,后缀表达式去掉了括号,但是确定了运算的优先级。数字运算的先后倒序排列,运算符运算的先后正序排列,让最先需要计算的数字遇到最先需要计算的运算符从而开始计算。计算过程不需要跳过任何符号,只要无脑从左往右遍历,遇到数字压栈,遇到运算符取出两个数字计算便可,是不是相当简单?

2.1中缀转后缀

那么最关键的就是如何得到这个后缀表达式。

下面列出算法(emmm我比较懒,没有去看过原论文,是参考的百度百科):

首先准备两个空栈S1和S2。S1作为临时储存运算符的栈,S2作为储存表达式的栈。下面就可以开始遍历表达式了,获取到的表达式通过以下规则压栈:

(1)如果遇到的是数字,直接压如S2中(放到S2里)

(2)如果遇到的运算符,比较该运算符和S1栈顶运算符的优先级(前面说这里的栈可以看做一个柱状容易,栈顶就是最后一个放入的大圆饼,括号的优先级最低),如果优先级大于栈顶符号,则直接压入S1中,否则的话弹出栈顶元素,直到栈顶元素优先级小于等于该运算符。弹出的元素直接压入S2中。

(3)如果遇到左括号,直接压入S1中。

(4)如果遇到右括号,将S1栈顶元素弹出并压入S2中,直到遇到左括号,然后左右括号全部丢掉即可。

(5)遍历完成后,S1的元素依次压入S2中。

还是上边的3+4*(2+3)为例:

6f0bfdaf41bb2a41514619bc87df4cf1.png

上面只是实现了最基本的四则运算,那要是遇到更复杂的乘方,开根,三角函数,指数,对数如何操作呢:

其实还是和上面相同,将特殊符号也看做是运算符,三角函数,指数,对数的优先级最高,然后是乘方和开根,然后是乘除,再然后才是加减。只不过在最后计算过程中,遇到三角函数,指数,对数这种只对一个数操作的运算符时,只弹出栈顶的一个数字即可。(注意:特殊运算符要用一个字符的代号表示,sin表示为s,cos表示为c)

以2+sin(3 + 2^2)/tan(exp(2))为例:

deb41d72727c4ecf3ab40febc8c35922.png

后缀表达式就是2,3,2,2,^,+,sin,2,exp,tan,/,+

然后计算后缀表达式:

31bdfbe10df20946213e25077a49ec1c.png

没有写出三角函数求出的具体值,为了和原式子进行比较。

最后栈内剩下的唯一数字就是最后的结果,于是便实现了一个最简单的科学计算器。

总结:

运算符优先级:

1.三角函数,指数函数,对数函数等函数操作

2.乘方,开方运算

3.乘除运算

4.加减运算

5.括号(看成运算符,并且级别最低,方便代码中书写)

下面通过java代码一步一步实现上述过程:

package 

新建四个ArrayList

下面的内容都是放在名叫Calculator里的

实例变量:

private 

本来可以用map储存上述结果,但是为了使用contains判断,还是分成了四个list。

名为setCalcu的方法,用来初始化四个list

public 

名为Change的方法:

//创建change函数把中缀表达式转化为后缀表达式

以上便完成了中缀表达式到后缀表达式的转换,由于正则表达式学的不好,这里没有使用,不然应该可以减少很多行代码,就像提取多位数那里。

下面运行下看看结果:

public 

0836ef074a4630a0df6a9c921b2270fb.png

和之前手算结果相同(s代表sin,e代表exp,t代表tan)

最后就是计算后缀表达式的值,计算之前需要定义一下运算符的计算方式,接下来就是无数个定义计算方式的方法:

public 

计算后缀表达式的方法:

public 

最后全部测试一遍:

public 

最终结果为:

130a9f6e4153ae1edea074660f99dfa4.png

结果完全正确!终于搞定了这些计算。一想到300多行的代码在python里只需要一行,就不禁有一点心痛啊。

代码是一部分一部分粘贴的,可能有一点乱,整个构造是:

package 

这个难点解决后,作图的app也成功完成。这里晒一下截图:

73851dade4cb1c713a42b0737244afb4.png

660d1d041994a8e4bd47a0bd60060f08.png

b51257d2f72ef09ee4cb78aaf8f89274.png

初学java和android,还没那么熟练,希望多多指教呀。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值