## 十、Python语言中简单数据结构的应用(之二)
----From a high schoolstudent's view to learn Python
关键字:
python 列表 堆栈 数据结构 递归调用 函数 组合问题 后缀表达式 前缀表达式逆波兰表达式 运算符 优先级
四、利用堆栈编程实现复杂的四则运算
本部分使用堆栈将表达式转换为后缀表达式,并计算表达式的值,表达式为四则运算表达式。
***Postfix notation***is an unambiguous way of writing an arithmetic expres- sion withoutparentheses. It is defined so that if“(*exp*1)**op**(*exp*2)”is a normal, fully parenthesized expression whose operation is**op**, the postfix version of this is“*pexp*1*pexp*2**op**”, where*pexp*1is the postfix version of*exp*1and*pexp*2is the postfix version of*exp*2.The postfix version of a sin- gle number or variable is just thatnumber or variable. For example, the postfix version of“((5+2)∗(8−3))/4”is “5 2 + 8 3 −∗4 /”. Describe a nonrecursiveway of evaluating an expression in postfix notation.
这是《Data Structures andAlgorithms in Python》书中堆栈的一道练习P252.
以下关于后缀表达式的详细说明(参考baidu)
1、概述
不包含括号,[运算符](http://baike.baidu.com/view/425996.htm)放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则,如:(2+ 1) * 3 , 即2 1 + 3 *
2、计算
运用后缀表达式进行计算的具体做法:
建立一个栈S 。从左到右读后缀表达式,如果读到[操作数](http://baike.baidu.com/view/420846.htm)就将它压入栈S中,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项按操作符运算,再将运算的结果代替原栈顶的n项,压入栈S中。如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束。
3、转换
计算机实现转换:
将中缀表达式转换为后缀表达式的算法思想:
·开始扫描;
·数字时,加入后缀表达式;
·运算符:
1. 若为 '(',入栈;
1. 若为')',则依次把栈中的的运算符加入后缀表达式中,直到出现'(',从栈中删除'(' ;
1. 若为 除括号外的其他[运算符](http://baike.baidu.com/view/425996.htm),当其优先级高于栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处理的[运算符优先级](http://baike.baidu.com/view/262524.htm)高和优先级相等的运算符,直到一个比它优先级低的或者遇到了一个左括号为止。
·当扫描的中缀表达式结束时,栈中的的所有运算符[出栈](http://baike.baidu.com/view/346791.htm);
4、人工实现转换
这里给出一个中缀表达式:a+b*c-(d+e)
第一步:按照[运算符](http://baike.baidu.com/view/425996.htm)的优先级对所有的运算单位加括号:式子变成了:((a+(b*c))-(d+e))
第二步:转换前缀与后缀表达式
- 前缀:把运算符号移动到对应的括号前面
则变成了:-( +(a *(bc))+(de))
把括号去掉:-+a*bc+de前缀式子出现
- 后缀:把运算符号移动到对应的括号后面
则变成了:((a(bc)* )+ (de)+)-
把括号去掉:abc*+de+-后缀式子出现
发现没有,前缀式,[后缀式](http://baike.baidu.com/view/2973716.htm)是不需要用括号来进行优先级的确定的。如表达式:3+(2-5)*6/3
5、计算机转换过程详细说明
后缀表达式 栈
3_________________+
3 ________________+(
3 2_______________+(-
3 2 5 -_____________+
3 2 5 -_____________+*
3 2 5 - 6 *___________+/
3 2 5 - 6 *3__________+/
3 2 5 - 6 *3/+________
("_____"用于隔开后缀表达式与栈)
遍历中缀表达式的每个节点,如果:
1)、 该节点为[操作数](http://baike.baidu.com/view/420846.htm):
直接拷贝进入后缀表达式
2)、 该节点是运算符,分以下几种情况:
A、 为“(”[运算符](http://baike.baidu.com/view/425996.htm):
压入临时堆栈中
B、 为“)”[运算符](http://baike.baidu.com/view/425996.htm):
不断地弹出临时堆栈顶部[运算符](http://baike.baidu.com/view/425996.htm)直到顶部的运算符是“(”为止。并把弹出的[运算符](http://baike.baidu.com/view/425996.htm)都添加到后缀表达式中
C、 为其他运算符,有以下步骤进行:
比较该运算符与临时栈栈顶指针的运算符的优先级,如果临时栈栈顶指针的优先级高于该运算符的优先级,弹出并添加到后缀表达式中,反复执行前面的比较工作,直到遇到一个栈顶指针的优先级低于该运算符的优先级,停止弹出添加并把该运算符压入栈中。
此时的比较过程如果出现栈顶的[指针](http://baike.baidu.com/view/159417.htm)为‘(’,则停止循环并把该运算符压入栈中,注意:‘(’不要弹出来。
遍历完中缀表达式之后,检查临时栈,如果还有[运算符](http://baike.baidu.com/view/425996.htm),则全部弹出,并添加到后缀表达式中。
以上描述的部分来自于网上的资料,写的非常详细,人工转换的例子也有。
五、使用Python的程序实现。
要把下面的仔细看完,还是需要一些耐心的。
首先是一些预处理的函数:目的是检查()使用是否匹配,有无不合法的字符出现在表达式中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # -*- coding=utf-8 -*-
def is_matched(expr): """ReturnTrue if all delimiters are properlymatch; Falseotherwise.""" lefty ='({[' righty =')}]' S =[] for c inexpr: if c inlefty: S.append(c) elif c inrighty: if len(S) ==0: returnFalse if righty.index(c) !=lefty.index(S.pop()): returnFalse returnlen(S) == 0
def validate (instr): """检查输入的表达式,将不合法的字符去掉 """ """对于-()以及(-())这样形式的字符串处理很麻烦,统一转换为(0-1)* """ op ='+-*/()' #合法的运算符 digital ='1234567890.' x1 ="" for i inrange(len(instr)): if instr[i] in op or instr[i]in digital: x1 = x1 + instr[i] x1 =x1.replace('(-', '((0-1)*') ifx1[0]=='-': x1 = x1.replace('-','(0-1)*', 1) returnx1
|
这个程序本身没有太多需要解释的,唯一需要注意的是:
line1:# -*- coding=utf-8-*-
这可不是注释,虽然以#开头,如果想让.py文件中使用中文的注释,而解释器不报错的话,必须在文件开头加入这么一句。
同时,如果想print语句显示中文字符串,必须注意些:
print u”中文显示举例”
在检查了表达式字符串的合法行之后,我们就将表达式中的数与运算符分离,按照各自的类型,逐个按照表达式的顺序存储在一个List中,如’123+234’,字符串123、234会被作为两个整型数保存。
程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | def separation (x): """ 将表达式进行拆分,拆分的结果存放到一个list中 由于list中的元素可以为各种类型,这给我们提供了极大的便利 将数字串转换为int或float型,运算符按字符串存储 """ op = '+-*/()' #合法的运算符 digital = '1234567890.' alist = [] ifrom = 999 for i in range(len(x)): if x[i] inop: if ifrom ==999: alist.append(x[i]) else: if x[i - 1] in digital: if '.' inx[ifrom:i]: alist.append( float(x[ifrom:i] ) ) else: alist.append( int( x[ifrom:i]) ) alist.append(x[i]) ifrom = 999 else: if ifrom==999: ifrom=i if ifrom != 999: if '.' inx[ifrom:len(x)]: alist.append( float(x[ifrom:len(x)] ) ) else: alist.append( int(x[ifrom:len(x)] ) ) return alist
|
最后就是后缀表达式转换的函数了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | defsuffix_expression_r(exprinlist): alist=[] blist=[] for i in exprinlist: if type(i)== float or type(i) == int: alist.append(i) if type(i)== str: if i=='(': blist.append(i) if i=='+' ori=='-': if len(blist)==0: blist.append(i) else: whileTrue: x=blist.pop() if x=='+' or x=='-' or x=='*'or x=='/': alist.append(x) if x=='(': blist.append(x) blist.append(i) break if len(blist)==0: blist.append(i) break if i=='*' ori=='/': if len(blist)==0: blist.append(i) else: whilelen(blist)>0: x=blist.pop() if x=='*' orx=='/': alist.append(x) blist.append(i) break if x=='+' orx=='-': blist.append(x) blist.append(i) break if x=='(': blist.append(x) blist.append(i) break if i==')': while len(blist)>0: x=blist.pop() ifx!='(': alist.append(x) ifx=='(': break while len(blist)>0: alist.append(blist.pop()) return alist
|
函数的输入参数是一个使用separation()函数将字符串表达式转换后的list
blist用来模拟存放运算符的堆栈,alist用来存放结果
最后要做的是进行计算,前面所有这些都是为了让计算机知道,哪些是数,哪些是运算操作符,并且将运算符的优先级顺序区分出来,等所有这些都做好,其实计算反而是最简单的事情了,看函数的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| defcalculation(expression): list_a=[] for i in expression: iftype(i)==float or type(i)==int: list_a.append(i) eliftype(i)==str: x=list_a.pop() y=list_a.pop() if i=='+': result=x+y if i=='-': result=y-x if i=='*': result=x*y if i=='/' andabs(x)>1E-9: result=y/x list_a.append(result) else: return 'Error' return result
|
函数调用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | expr ="3+(2-5)*6.0/3" if notis_matched(expr): print u"错误的表达式!" else: temp_x1=validate(expr) print(temp_x1) expr_list = separation(temp_x1) print expr_list print suffix_expression_r(expr_list) s_expr =suffix_expression_r(expr_list) print s_expr print expr, ' = ',calculation(s_expr)
|
显示的运行结果如下:
3+(2-5)*6.0/3
[3, '+', '(', 2, '-', 5, ')','*', 6.0, '/', 3]
[3, 2, 5, '-', 6.0, '*', 3,'/', '+']
3+(2-5)*6.0/3 = -3.0
六、结束语
看着、想着都很简单的事,为什么让计算机做起来这么复杂呢?甚至都会觉得计算机一点也没有我之前很智能的感觉了。但是在把这个程序做好之后,你输入任一的四则运算表达式,不管多长,它都能瞬间的检查你的表达式是否正确,如果正确的话它也能够瞬间的给你答案,似乎又智能了。
所以,我们应该这么认为:计算机的计算能力是很强大的,关键在于我们能够让它做什么,这个让计算机做什么的过程就是编程的过程。
我的更多文章:
- [Python程序调试的一些体会](http://blog.sina.com.cn/s/blog_d6cca93e0101ewc9.html)(2013-10-06 22:57:35)
- [十四、Python编程计算24点(之二)](http://blog.sina.com.cn/s/blog_d6cca93e0101euxx.html)(2013-10-03 22:18:28)
- [十三、Python编程计算24点(之一)](http://blog.sina.com.cn/s/blog_d6cca93e0101eukc.html)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1cc04fc3.gif "此博文包含图片")
(2013-10-02 22:15:46)
- [十一、Python简单数据结构应用(之一)](http://blog.sina.com.cn/s/blog_d6cca93e0101ep9z.html)(2013-09-23 23:31:49)
- [十、Python编程解决组合问题(之二)](http://blog.sina.com.cn/s/blog_d6cca93e0101entc.html)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1cc04fc3.gif "此博文包含图片")
(2013-09-21 23:37:27)
- [九、Python编程解决组合问题(之一)](http://blog.sina.com.cn/s/blog_d6cca93e0101ent7.html)(2013-09-21 23:32:54)
- [八、Python的函数编程(之二)](http://blog.sina.com.cn/s/blog_d6cca93e0101ekwj.html)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1cc04fc3.gif "此博文包含视频")
(2013-09-20 23:09:39)
- [七、Python的函数编程(之一)](http://blog.sina.com.cn/s/blog_d6cca93e0101ekwg.html)![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1cc04fc3.gif "此博文包含视频")
(2013-09-20 23:09:10)
- [六、Python的程序流程](http://blog.sina.com.cn/s/blog_d6cca93e0101ejeg.html)(2013-09-19 16:53:58)
- [高中生如何学编程](http://blog.sina.com.cn/s/blog_d6cca93e0101e8fn.html)(2013-09-02 19:26:01)
';