算术表达式求值问题z数据结构与算法课程设计报告.docx
- 文档编号:11400696
- 上传时间:2023-02-28
- 格式:DOCX
- 页数:26
- 大小:128.97KB
算术表达式求值问题z数据结构与算法课程设计报告.docx
《算术表达式求值问题z数据结构与算法课程设计报告.docx》由会员分享,可在线阅读,更多相关《算术表达式求值问题z数据结构与算法课程设计报告.docx(26页珍藏版)》请在冰豆网上搜索。
算术表达式求值问题z数据结构与算法课程设计报告
合肥学院
计算机科学与技术系
课程设计报告
2009~2010学年第二学期
课程
数据结构与算法
课程设计名称
算术表达式求值问题
学生姓名
杨松
学号
0804012031
专业班级
08计科
(2)
指导教师
王昆仑张贯虹
2010年6月
题目:
(算术表达式求值问题)一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。
假设操作数是正整数,运算符只含加减乘除四种运算符,界限符有左右括号和表达式起始、结束符“#”,如:
#(7+15)*(23-28/4)#。
引入表达式起始、结束符是为了方便。
编程利用“算符优先法”求算术表达式的值。
要求:
(1)从键盘读入一个合法的算术表达式,输出正确的结果。
(2)显示输入序列和栈的变化过程。
选作内容:
操作数类型扩充到实数。
一、问题分析和任务定义
要对一个含有加减乘除四则运算的合法的算术表达式进行求值,
首先,应了解算术表达式的四则运算的规则:
(1)从左向右计算
(2)先乘除,后加减
(3)先括号内,后括号外
由此可知,比如算术表达式(7+15)*(23-28/4)的计算顺序,即为
(7+15)*(23-28/4)=22*(23-28/4)=22*(23-7)=22*16=352
其次,应明确“算符优先法”的内容:
算符优先法就是根据上述四则运算规则确定的优先关系来实现对表达式的编译或解释执行的。
一个简单的四则运算表达式由操作数(operand)、运算符(operator)和界限符(delimiter)组成,其中操作数是正整数,运算符只含加、减、乘、除四种,界限符有左右括号和表达式起始、结束符“#”;而且,为了统一算法的描述,将运算符和界限符通称为算符。
算符集OP={+,-,*,/,(,),#}。
根据上述3条运算规则,在具体的运算的每一步中,任意两个相继出现的算符θ1和θ2之间的优先关系只能是如下3中关系之一:
θ1<θ2θ1的优先级低于θ2
θ1=θ2θ1的优先级等于θ2
θ1>θ2θ1的优先级高于θ2
下表定义了算符之间的这种优先关系。
表1算符之间的优先关系
θ1
θ2
+
-
*
/
(
)
#
+
>
>
<
<
<
>
>
-
>
>
<
<
<
>
>
*
>
>
>
>
<
>
>
/
>
>
>
>
<
>
>
(
<
<
<
<
<
=
)
>
>
>
>
>
>
#
<
<
<
<
<
=
表格说明:
1、θ1、θ2同“*”、“/”或同为“+”、“-”,则算符θ1的优先级高于θ2
2、θ1为“*”、“/”的优先级高于θ1为“+”、“-”
3、θ1为“+”、“-”、“*”、“/”的优先级高于θ2为“)”
4、θ1为“(”的优先级低于θ2为“+”、“-”、“*”、“/”
5、θ1、θ2同为“(”,θ1的优先级低于θ2;θ1、θ2同为“(”,θ1的优先级高于θ2
6、θ1为“(”且θ2为“)”,或者,θ1、θ2同为“#”,则θ1、θ2的优先级相同
7、θ1为“)”、θ2为“(”,或者,θ1为“#”θ2为“)”,或者θ1为“(”θ2为“#”,这3中情况各自之间无优先关系,表示为“”,因为表达式中不允许它们相继出现,如果出现,则可以认为出现语法错误。
最后,确定算法的基本思想:
设置两个工作栈,一个为OPTR栈,存放运算符;另一个为OPND栈,存放操作数或运算结果。
则算法的基本思想描述如下:
(1)首先置操作数栈为空栈,表达式起始符“#”为运算符栈的栈底元素;
(2)依次读入表达式中每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先级后,做如下相应操作:
若栈顶算符θ1的优先级低于刚读入的算符θ2,则将θ2入算符栈OPTR
若栈顶算符θ1的优先级高于刚读入的算符θ2,则将让θ2出栈;同时,将操作数栈OPND退栈两次,得到两个操作数x、y,对x、y运用θ2进行运算后,再将运算结果如操作数只栈OPND
若栈顶算符算符θ1的优先级等于刚读入的算符θ2,说明左右括号相遇,或者是表达式的起始、结束符相遇,只需将栈顶算符(左括号或起始符)退栈即可;当算符栈OPTR栈空时,算法结束。
二、数据结构的选择和概要设计
数据结构的选择:
本程序采用顺序存储结构存储两个栈,即操作数栈(OPND)和算符栈(OPTR);
概要设计如下:
运算符栈的相关功能函数
操作数栈的相关功能函数
算术表达式的相关功能函数
主函数
表2运算符栈的功能函数
OPTR栈
intOPTR_InitStack(OPTR_STACK&s)
算符栈的初始化
charOPTR_GetTop(OPTR_STACK&s)
获取算符栈的栈顶元素
intOPTR_Push(OPTR_STACK&s,chare)
算符栈的栈顶插入新的数据元素
intOPTR_Pop(OPTR_STACK&s,char&e)
算符栈的栈顶元素出栈
表3操作数栈的功能函数
OPND栈
intOPND_InitStack(OPND_STACK&s)
操作数栈的初始化
doubleOPND_GetTop(OPND_STACK&s)
获取操作数栈的栈顶元素
intOPND_Push(OPND_STACK&s,doublee)
操作数栈的栈顶插入新的数据元素
intOPND_Pop(OPND_STACK&s,double&e)
操作数栈的栈顶元素出栈
表4算术表达式的相关功能函数
doubleOperate(doublea,chartheta,doubleb)
两个操作数之间的四则运算函数
intIn(charTest,char*TestOp)
算符的判断函数
intReturnOpOrd(charop,char*TestOp)
返回该算符在算符数组中的位置的函数
charPrecede(charAop,charBop)
两个算符的优先级的判断函数
doubleEvaluateExpression(char*MyExpression)
表达式的正确性判断函数,同时计算表达式
三、详细设计和编码
首先本程序定义两个顺序栈:
算符栈(OPTR)和操作数栈(OPND);
OPTR栈定义如下:
typedefstruct{
char*base;//算符栈的栈底
char*top;//算符栈的栈顶
intstacksize;//算符栈的最大长度
}OPTR_STACK;
OPND栈定义如下:
typedefstruct{
double*base;//操作数栈的栈底
double*top;//操作数栈的栈顶
intstacksize;//操作数栈的最大长度
}OPND_STACK;
然后是算符栈和操作数栈的相关功能函数的详细设计:
(1)算符栈的相关功能函数的详细设计如下:
1、intOPTR_InitStack(OPTR_STACK&s)
为OPTR栈申请char类型的初始空间STACK_INITSIZE=100个
操作结果:
构造一个空栈S,最大长度s.stacksize为100;
2、charOPTR_GetTop(OPTR_STACK&s)
初始条件:
OPTR栈S已存在且非空(s.top!
=s.base)
操作结果:
用e=*(s.top-1)返回S的栈顶元素
3、intOPTR_Push(OPTR_STACK&s,chare)
初始条件:
OPTR栈S已存在
因为考虑到原先申请的空间可能不够,即当OPTR栈的长度已经大于s.stacksize=100,这是需要申请额外的存储空间;每次均用realloc函数申请char类型的额外的STACKINCREMENT=10个存储单元,申请额外空间后的OPTR栈的最大长度s.stacksize增加10;
操作结果:
插入元素e为新的OPTR栈顶元素,即*s.top++=e;
4、intOPTR_Pop(OPTR_STACK&s,char&e)
初始条件:
OPTR栈S已存在且非空;
操作结果:
OPTR栈顶元素出栈,同时用e保存该栈顶元素,即e=*--s.top;
(2)操作数栈的相关功能函数的详细设计如下:
1、intOPND_InitStack(OPND_STACK&s)
为OPTR栈申请double类型的初始空间STACK_INITSIZE=100个
操作结果:
构造一个空栈S,最大长度s.stacksize为100;
2、doubleOPND_GetTop(OPND_STACK&s)
初始条件:
OPND栈S已存在且非空(s.top!
=s.base)
操作结果:
用e=*(s.top-1)返回S的栈顶元素
3、intOPND_Push(OPND_STACK&s,doublee)
初始条件:
OPND栈S已存在
因为考虑到原先申请的空间可能不够,即当OPND栈的长度已经大于s.stacksize=100,这是需要申请额外的存储空间;每次均用realloc函数申请double类型的额外的STACKINCREMENT=10个存储单元,申请额外空间后的OPND栈的最大长度s.stacksize增加10;
操作结果:
插入元素e为新的OPND栈顶元素,即*s.top++=e;
4、intOPND_Pop(OPND_STACK&s,double&e)
初始条件:
OPND栈S已存在且非空;
操作结果:
OPND栈顶元素出栈,同时用e保存该栈顶元素,即e=*--s.top;
(3)下面实现本课程设计目标的算术表达式的相关功能函数的详细设计过程:
首先应定义7种算符的字符数组charOPSET[OPSETSIZE]={'+','-','*','/','(',')','#'};
然后是7算符的的优先级的定义:
charPrior[7][7]={
'>','>','<','<','<','>','>',
'>','>','<','<','<','>','>',
'>','>','>','>','<','>','>',
'>','>','>','>','<','>','>',
'<','<','<','<','<','=','',
'>','>','>','>','','>','>',
'<','<','<','<','<','','='
};
因为算术表达式涉及到两个数的四则运算,所以要设计一个两个数四则运算的函数,即函数doubleOperate(doublea,chartheta,doubleb),定义了两个数的(加)+、(减)-、(乘)*、(除)/四种运算,并返回两个数的运算结果。
然后,当依次读入算术表达式的各个字符时,要同时判断字符的合法性,包括算符的合法性和操作数的合法性,所以应设计算符合法性的判断函数,即intIn(charTest,char*TestOp),将依次读入的字符与算符数组中的所有算符依次比较,若算符合法,则返回该算符在算符数组中的位置m,若算符不合法,则均返回0。
在判断算符的合法性之后,则应该对相邻的两个算符的优先级进行比较,以便于算术表达式的相关运算,所以应设计函数charPrecede(charAop,charBop)比较两个算符Aop和Bop的优先级,返回值即为两个算符对应在算符优先级二维数组Prior[7][7]中的值。
经过上述两个步骤,确定了的算符的合法性与否以及两个算符的优先级,然后即要判断算术表达式的合法性与否,并同时对该算术表达式进行一系列的计算,设计函数doubleEvaluateExpression(char*MyExpression)。
该函数需要的所有变量为:
OPTR,为算符栈;OPND,为操作数栈;TempData[40],为辅助字符数组,其中所有的字符均为满足合法性的算术表达式的操作数;theta,为算符;a,b,为运算的两个操作数;Data,为两个数的运算结果;c,为当前读入的字符;Dr[2],为当前读入的字符和结束符’\0’构成的字符串;其中变量TempData[40]和Dr[2]是因为考虑到操作数有可能不止一位数,则Dr[2]起到字符衔接的作用,只要读入的字符是操作数,就将该字符衔接到字符数组TempData[40]中,则TempData[40]中除了结束符’\0’之外的所有字符就是算术表达式中的一个完整的操作数,即TempData[40]中存放的操作数或者是一位数,或者是多位数。
算术表达式的详细计算过程:
首先,将所需的变量初始化:
初始化算符栈和操作数栈,OPTR_InitStack(OPTR),OPND_InitStack(OPND);将算术表达式的起始字符’#’入算符栈;变量c保存当前表达式MyExpression的地址;利用strcpy()函数将结束符’\0’衔接至数组TempData[40]中。
然后,利用while循环,开始计算。
当OPTR栈的栈顶元素OPTR_GetTop(OPTR)和当前读入的字符*c同时为’#’时,整个表达式求值完毕;若不满足表达式结束的该条件,则进行计算。
读入当前字符*c:
若*c是操作数,则首先将*c给Dr[2],利用strcat()函数将Dr[2]衔接至TempData[40]中,然后c++,判断下一个字符*c是否是算符,若不是,说明当前操作数是多位数,然后再将该操作数字符衔接至TempData[40]中,然后再c++,重复上述过程,将当前操作数的所有字符完整的保存至TempData[40]中,利用强制转换函数atof()将TempData[40]中的字符转换为double型的数据,同时赋给Data,则Data即为当前的完整的double型操作数(Data或为一位数,或为多位数),然后Data入操作数栈,即OPND_Push(OPND,Data);
若*c是算符,则将*c与当前算符栈的栈顶元素进行优先级的比较,即Precede(OPTR_GetTop(OPTR),*c);比较的结果若为’<,说明当前算符栈的栈顶算符的优先级低于比当前读入的字符*c的优先级,则将*c入栈,即OPTR_Push(OPTR,*c),且c++;若比较结果为’=’,说明当前算符栈的栈顶算符的优先级等于比当前读入的字符*c的优先级,则将当前算符栈的栈顶算符出栈,即OPTR_Pop(OPTR,x),且c++;若比较结果为’>’,说明当前算符栈的栈顶算符的优先级高于比当前读入的字符*c的优先级,则首先将当前算符栈的栈顶算符出栈,即OPTR_Pop(OPTR,theta);然后,将当前操作数栈的栈顶操作数和次栈顶操作数依次出栈,即OPND_Pop(OPND,b),OPND_Pop(OPND,a),将该两个操作数a和b进行theta运算,将运算结果入操作数栈,即OPND_Push(OPND,Operate(a,theta,b));
while循环重复上述两个过程,直至当前算符栈的栈顶元素OPTR_GetTop(OPTR)和当前读入的字符*c同时为’#’时,整个算术表达式的计算全过程结束。
四、上机调试
1、语法问题及解决
由于本程序牵扯到四个方面的内容,首先是OPTR栈的相关功能函数的设计;其次是OPND栈的相关功能函数的设计;再次是算术表达式的具体计算涉及到的相关功能函数的设计,最后是主函数的设计;前两个方面均为对顺序栈的基本操作,具有通用性,基本无语法错误;主要是在设计算术表达式的具体计算的函数时,调用相关栈的函数以及其它语句时,出现了语法编辑的错误,还有在设计主函数的时候,由于要考虑到用户的使用方便,也出现了一些编辑语法错误,在反复使用加注释和单步运行两种方法对于具体错误分析、调试后,所有语法错误均得到解决。
2、逻辑问题及解决
在编辑本程序时,一个最关键的方面就是在设计本程序的中心算法即算术表达式的具体计算函数EvaluateExpression(char*MyExpression)时,出现多处逻辑错误;其中最关键的两个问题就是,如何解决操作数是实数范围内的多位数处理问题,和读入的非法字符(包括非法算符和非法操作数)的处理问题;经反复思考和多次查阅资料,解决办法:
一方面,利用在程序的详细设计过程中提到的TempData[40]和Dr[2],将依次读入的字符满足操作数的条件时,就利用Dr[2]将该字符衔接至TempData[40]中,这样直到读入的字符是算符时,TempData[40]中存放的所有字符组成的字符串就是一个完整的操作数(或是实数范围内的一位数,或是实数范围内的多位数;或是实数范围内的整数,或是实数范围内的浮点数),这样就解决了操作数是实数范围内的多位数处理问题;另一方面,利用强制转换函数atof(),将TempData[40]中所有字符构成的字符串强制转换为double型浮点数据,就将读入字符过程中出现的非法字符(非法算符和非法操作数)全部转换为实数0.000,它们就不会影响到合法的数据,更不会影响到后继读入的字符的处理,这样就解决了读入的非法字符的处理问题。
3、时间、空间复杂度的分析
由于本程序的算法只涉及到2个循环语句,分别为:
for(inti=0;i ='#'||OPTR_GetTop(OPTR)! ='#'),此循环语句是用于算术表达式的具体计算,它的时间复杂度取决于用户输入的算术表达式MyExpression的长度length,length=strlen(MyExpression),即该语句的时间复杂度为O(length)=O(strlen(MyExpression)); 所以,本程序的算法的时间复杂度为O(OPSETSIZE+strlen(MyExpression))。 由于在本程序中,在为算符栈(OPTR)和操作数栈(OPND)涉及到两种情况时申请空间,一方面分别为OPTR栈和OPND栈申请了初始的存储单元,均为STACK_INITSIZE=100个;另一方面,考虑到两个栈在处理具体的算术表达式时,有可能会出现溢出的情况,即栈的初始的存储空间不够用,这时需要为其申请额外的存储空间,每溢出一次,就为其申请存储单元STACKINCREMENT=10个;所以,本程序的算法的空间复杂度一方面取决于算术表达式的长度,另一方面取决于本程序的所有代码所占用的存储空间大小。 五、测试结果及其分析 调试菜单: 第一组测试数据: 2.34+11.456=13.796 该算术表达式为单纯的加法运算,参与运算的两个数据均为实数,算术表达式的结果为13.796,正确;而且算术表达式的具体计算过程即算符栈(OPTR)和操作数栈(OPND)的入栈和出栈的遍历过程均正确; 第二组测试数据: 999.567-10.43=989.137 该算术表达式为单纯的减法运算,参与运算的两个数据均为实数,算术表达式的结果为989.137,正确;而且算术表达式的具体计算过程即算符栈(OPTR)和操作数栈(OPND)的入栈和出栈的遍历过程均正确; 第三组测试数据: 23.24*34.5=801.780 该算术表达式为单纯的乘法运算,参与运算的两个数据均为实数,算术表达式的结果为801.780,正确;而且算术表达式的具体计算过程即算符栈(OPTR)和操作数栈(OPND)的入栈和出栈的遍历过程均正确; 第四组测试数据: 1000.46/10.2=98.084 该算术表达式为单纯的除法运算,参与运算的两个数据均为实数,算术表达式的结果为98.084,正确;而且算术表达式的具体计算过程即算符栈(OPTR)和操作数栈(OPND)的入栈和出栈的遍历过程均正确; 第五组测试数据: 1.21+(12.3/3-4)*2=-1.410 该算术表达式为加减乘除的复合运算,参与运算的所有数据均在实数范围内,算术表达式的结果为1.410,正确;而且算术表达式的具体计算过程即算符栈(OPTR)和操作数栈(OPND)的入栈和出栈的遍历过程均正确; 第六组测试数据: (7+15)*(23-28/4)=352 该算术表达式为加减乘除的复合运算,参与运算的所有数据均在实数范围内,并且均是正整数,算术表达式的结果为352.000,正确;而且算术表达式的具体计算过程即算符栈(OPTR)和操作数栈(OPND)的入栈和出栈的遍历过程均正确。 六、用户使用说明 1、本课程设计源代码重要语句均有相应的注释,用户可根据注释加以使用、阅读和理解; 2、本课程设计源程序适用于实数范围内double浮点型数据的四则运算,非常方便用户使用; 3、本课程设计考虑到了多种特殊情况的处理和意外错误的处理;同时在调试菜单过程中也进行了特殊处理,可便于用户参考,详细情况见“详细设计和编码”。 七、参考文献 [1]王昆仑,李红.数据结构与算法.北京: 中国铁道出版社,2006年5月。 [2]严蔚敏,吴伟民.数据结构(C语言版).北京: 清华大学出版社,2007年5月。 八、附录 #include #include #include #defineSTACK_INITSIZE100//存储空间初始分配量 #defineSTACKINCREMENT10//存储空间分配量 #defineOPSETSIZE7//算符的长度 /********************算符栈的结构体**********************/ typedefstruct{ char*base;//算符栈的栈底 char*top;//算符栈的栈顶 intstacksize;//算符栈的最大长度 }OPTR_STACK; /********************操作数栈的结构体********************/ typedefstruct{ double*base;//操作数栈的栈底 double*top;//操作数栈的栈顶 intstacksize;//操作数栈的最大长度 }OPND_STACK; intOPTR_InitStack(OPTR_STACK&s){//算符栈的初始化函数 s.base=(char*)malloc(STACK_INITSIZE*sizeof(char));//算符栈分配初始的存储空间 if(! s.base)return0;//若申请存储空间失败,返回0 s.top=s.bas
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算术 表达式 求值 问题 数据结构 算法 课程设计 报告