十二、Python简单数据结构应用(之…

最后更新于:2022-04-01 21:47:42

## 十、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)
';