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

十、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、转换

计算机实现转换:

将中缀表达式转换为后缀表达式的算法思想:

·开始扫描;

·数字时,加入后缀表达式;

·运算符:

  1. 若为 "(",入栈;
  2. 若为")",则依次把栈中的的运算符加入后缀表达式中,直到出现"(",从栈中删除"(" ;
  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

六、结束语

看着、想着都很简单的事,为什么让计算机做起来这么复杂呢?甚至都会觉得计算机一点也没有我之前很智能的感觉了。但是在把这个程序做好之后,你输入任一的四则运算表达式,不管多长,它都能瞬间的检查你的表达式是否正确,如果正确的话它也能够瞬间的给你答案,似乎又智能了。

所以,我们应该这么认为:计算机的计算能力是很强大的,关键在于我们能够让它做什么,这个让计算机做什么的过程就是编程的过程。

我的更多文章:

文章导航