数据结构视频(5)和(9)堆栈应用程序:支持表达式评估的学习文档

这是以下两个数据结构学习视频的支持文档:

(5)堆栈应用:表达式求值预览

(9)堆栈应用:表达式求值说明

请同学们先阅读文档再学习视频效果更好。(请用电脑查看此文档)

一、表达式求值的规则

      表达式求值是程序设计语言编译中的一个基本问题。它的实现就是对“栈”的典型应用。

      首先了解算术四则运算的运算规则:

     (1)先乘除,后加减。

     (2)从左到右计算

     (3)先算括号内,再算括号外

      因此,下面这个算数表达式的计算顺序应为:

      4 + 2 * 3 – 10 / 5

      = 4 + 6 – 10 / 5

      = 10 – 10 / 5

      = 10 – 2

      = 8

    任何一个表达式都由操作数(operand)、运算符(operator)和界定符组成:

  ①操作数即可以是常量,也可以是被说明为变量或常量的标识符。

  ②运算符可以分为算术运算,关系运算和逻辑运算符。

  ③界定符有左右括号和结束符等。

      为了叙述的简洁,我们仅讨论简单算数表达式的求值问题,这种表达式只包含加、减、乘、除等四种算术运算。需要时,不难把它推广到更一般的表达式上。

二、运算符优先级

    对于两个相继出现的操作符θ1和θ2 有三种关系:

    θ1 <θ2    θ1的优先级低于θ2

    θ1 =θ2    θ1的优先级等于θ2

    θ1 >θ2    θ1的优先级高于θ2

    由此可以列出“+-*/”之间的优先级。如下图(见图1)

    通过图可以看出:加减乘除优先性都低于“(”但是高于“)”,由运算从左到右可知,当θ1=θ2 ,令θ1>θ2;

    为了算法简洁,在表达式的左边和右边虚设一个“#”,这一对“#”表示一个表达式求值完成。

    “(”=“)”当一对括号相遇时表示括号内已运算完成。

     “)”和“(”、“#”和“(”、“(”和“#”无法相继出现,如果出现则表达式出现语法错误。(实现时,可以用0存储)

    这个表如何理解呢?例如:a+b+c,这里有两个运算符,运算符θ1 为+,运算符θ2 也为+ ,查上表,得到“>”的关系,那么意味着先计算前面的+号,也就是先算a+b,得到结果后,再考虑算后面的表达式。

三、算法思路

    为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存操作数和运算结果。算法的基本思想是:

    (1) 首先置操作数栈为空栈,表达式起始符’#’为栈底元素。

    (2)依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先级作相应操作,直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为’#’)

    算数表达式求值的运算符优先算法。假定输入的表达式语法正确,以’#’做结束符。使用OPTR和OPND分别作为运算符栈和操作数栈。

    expression函数的算法逻辑如下。以下是伪代码,需要用真实的代码代替:

setNull(OPTR);

push(OPTR, ‘#’);  //将’#’压入操作符OPTR栈

setNull(OPND);

读入字符ch;

do{

      if (ch IN op) //op为运算符的集合

            swith( precede(top(OPTR),ch ) { //比较栈顶元素和ch的优先关系

            case ‘<‘:

                  push(OPTR,ch);  //栈顶元素优先级低,则压入操作符栈

                  读入字符ch;

                  break;

            case ‘=’:

                  if (ch==’)’) 

                  x = pop(OPTR);   //自己思考什么情况需要这个判断

                  读入字符ch;

                  break;

            case ‘>’://栈顶元素优先级高,取出一个运算符,两个操作数,并计算

                  theta = pop(OPTR);

                  b = pop(0PND);

                  a = pop(OPND);

                  push(OPND,operate(a, theta, b));//将计算结果压入操作数栈

      }

      else{   //op为操作数的集合

            push(OPND,ch);

            读入字符ch;

      }

}while (( ch != ‘#’) OR ( top(OPTR) != ‘#’))

return top(OPND);

    算法中调用了两个函数,precede是判断运算符栈的栈顶运算符与读入的运算符之间的优先关系;

    operate作一元运算:a θ b,算出运算结果,例如调用operate(1,+,5);算出结果6。

四、编程需要思考的几个问题

1.优先级表如何存储并查找?(precede函数)

2.如何判断输入的字符是数字还是运算符?(isOpnd,isOptr函数)

3.根据举例手动跟踪代码,看看能否理解栈的变化情况。

4.如何实现operate函数?

5.目前的算法是在假定表达式正确的情况下进行的,要求大家先这样假定,(1)只对1位整数进行四则运算,采用跟踪调试的方法检验程序的运行。当程序运行正确后,再根据自己的能力情况考虑增加:(2)是否要对空格过滤?(3)是否要对多位整数进行运算?(4)是否需要检查表达式格式是否正确?按照提示的(2)(3)(4)顺序逐步增加功能,每增加一个检查后,用多个范例验证过关后,再进入下一个步骤,不要一次把所有的检查功能都加上去,这样会增加调试难度。

即使只完成(1)的功能也算程序完成,但一定要做到能够想明白程序是如何执行的,堆栈是如何变化的。

五、测试范例

说明:蓝色字体表示输入,黑色字体是说明,不是运行结果的一部分。基本要求完成(1)的效果,如果完全正确完成(1)可以评90。后续其他范例可选,还有其他范例,可以自己思考和检查。

(1)1位整数的表达式

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

2+3*4*(5-2)+6#

计算结果是:44

(2)包含空格

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

2   +3 *9 #

计算结果是:29

(3)识别多位整数

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

23*(2+5)+62/3#

计算结果是:181.667

(4)数字中包含空格

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

23  *4 5+2#

计算结果是:1037

(5)检查表达式格式是否正确

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

@345+6#

输入错误,请输入一个正确的表达式(可以输入的字符包括+-*/()#0123456789)。

计算结果是:-1

(6)检查表达式格式是否正确

请输入一个正整数表达式(可以输入的字符包括+-*/()#0123456789):

#34/3#

输入错误,表达式前面不能以#开头。

计算结果是:-1

参考文献:

[1]严蔚敏.数据结构(C语言版)[M].清华大学出版社,北京:2019

 

 

 

 

资源下载: