十、Python语言中简单数据结构的应用(之二)
----From a high schoolstudent"s view to learn Python
关键字:
python 列表 堆栈 数据结构 递归调用 函数 组合问题 后缀表达式 前缀表达式逆波兰表达式 运算符 优先级
四、利用堆栈编程实现复杂的四则运算
本部分使用堆栈将表达式转换为后缀表达式,并计算表达式的值,表达式为四则运算表达式。
Postfix notationis an unambiguous way of writing an arithmetic expres- sion withoutparentheses. It is defined so that if“(exp1)op(exp2)”is a normal, fully parenthesized expression whose operation isop, the postfix version of this is“pexp1pexp2op”, wherepexp1is the postfix version ofexp1andpexp2is the postfix version ofexp2.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、概述
不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则,如:(2+ 1) 3 , 即2 1 + 3
2、计算
运用后缀表达式进行计算的具体做法:
建立一个栈S 。从左到右读后缀表达式,如果读到操作数就将它压入栈S中,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项按操作符运算,再将运算的结果代替原栈顶的n项,压入栈S中。如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束。
3、转换
计算机实现转换:
将中缀表达式转换为后缀表达式的算法思想:
·开始扫描;
·数字时,加入后缀表达式;
·运算符:
- 若为 "(",入栈;
- 若为")",则依次把栈中的的运算符加入后缀表达式中,直到出现"(",从栈中删除"(" ;
- 若为 除括号外的其他运算符,当其优先级高于栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,直到一个比它优先级低的或者遇到了一个左括号为止。
·当扫描的中缀表达式结束时,栈中的的所有运算符出栈;
4、人工实现转换
这里给出一个中缀表达式:a+b*c-(d+e)
第一步:按照运算符的优先级对所有的运算单位加括号:式子变成了:((a+(b*c))-(d+e))
第二步:转换前缀与后缀表达式
- 前缀:把运算符号移动到对应的括号前面
则变成了:-( +(a *(bc))+(de))
把括号去掉:-+a*bc+de前缀式子出现
- 后缀:把运算符号移动到对应的括号后面
则变成了:((a(bc)* )+ (de)+)-
把括号去掉:abc*+de+-后缀式子出现
发现没有,前缀式,后缀式是不需要用括号来进行优先级的确定的。如表达式: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)、 该节点为操作数:
直接拷贝进入后缀表达式
2)、 该节点是运算符,分以下几种情况:
A、 为“(”运算符:
压入临时堆栈中
B、 为“)”运算符:
不断地弹出临时堆栈顶部运算符直到顶部的运算符是“(”为止。并把弹出的运算符都添加到后缀表达式中
C、 为其他运算符,有以下步骤进行:
比较该运算符与临时栈栈顶指针的运算符的优先级,如果临时栈栈顶指针的优先级高于该运算符的优先级,弹出并添加到后缀表达式中,反复执行前面的比较工作,直到遇到一个栈顶指针的优先级低于该运算符的优先级,停止弹出添加并把该运算符压入栈中。
此时的比较过程如果出现栈顶的指针为‘(’,则停止循环并把该运算符压入栈中,注意:‘(’不要弹出来。
遍历完中缀表达式之后,检查临时栈,如果还有运算符,则全部弹出,并添加到后缀表达式中。
以上描述的部分来自于网上的资料,写的非常详细,人工转换的例子也有。
五、使用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程序调试的一些体会(2013-10-06 22:57:35)
- 十四、Python编程计算24点(之二)(2013-10-03 22:18:28)
- 十三、Python编程计算24点(之一)
(2013-10-02 22:15:46) - 十一、Python简单数据结构应用(之一)(2013-09-23 23:31:49)
- 十、Python编程解决组合问题(之二)
(2013-09-21 23:37:27) - 九、Python编程解决组合问题(之一)(2013-09-21 23:32:54)
- 八、Python的函数编程(之二)
(2013-09-20 23:09:39) - 七、Python的函数编程(之一)
(2013-09-20 23:09:10) - 六、Python的程序流程(2013-09-19 16:53:58)
- 高中生如何学编程(2013-09-02 19:26:01)