算术表达式求值问题课程设计报告.docx
- 文档编号:12165524
- 上传时间:2023-04-17
- 格式:DOCX
- 页数:23
- 大小:144.46KB
算术表达式求值问题课程设计报告.docx
《算术表达式求值问题课程设计报告.docx》由会员分享,可在线阅读,更多相关《算术表达式求值问题课程设计报告.docx(23页珍藏版)》请在冰豆网上搜索。
算术表达式求值问题课程设计报告
合肥学院
算术表达式求值演示
一、问题分析和任务定义
实验题目:
算术表达式求值:
一个算术表达式是由操作数(operand),运算符(operator)和界限符(delimiter)组成的。
假设操作数是正整数,运算符只含加减乘除等四种运算符,界限符有左右括号和表达式起始,结束符“#”,如:
#(7+15)*(23-28/4)#。
引入表达式起始、结束符是为了方便。
编程利用“算符优先法”求算术表达式的值。
要求:
(1)从键盘读入一个合法的算术表达式,输出正确的结果。
(2)显示输入序列和栈的变化过程。
选作内容:
操作数类型扩充到实数。
问题分析:
在带括号的的算术表达式中,界限符包括左右括号以及表达式起始、结束符“#”。
假设运算符只有加、减、乘、除4种,则对一个简单的算术表达式的运算规则如下:
(1)从左至右运算表达式。
(2)先乘、除,后加、减。
(3)先括号内,后括号外。
要想能够实现这个问题,首先,你要从键盘中输入一个字符并判别该字符是运算符还是运算数。
如果是运算数就直接进运算数栈。
如果是运算符,则与运算符栈的栈顶元素进行优先级的比较(可以单独写一个优先级比较函数,将每个运算符与其他运算符之间的优先级一一比较出来,可以设置为“>”、“<”、“=”),如果比较后为“>”,则将运算数栈依次出栈两次,将该运算符和刚出栈的两个运算数进行计算(单独写一个函数,将“+”、“-”、“*”、“/”四种情况一一写出来),然后将计算的结果入运算数栈,将读入的运算符入运算符栈;如果比较后为“<”,则将该运算符入运算符栈;如果是“=”,同“>”,但要注意如果从运算符栈出栈的元素为“#”和“(”,则运算数栈无需出栈,并且运算符栈也无需入栈。
按照上面的步骤,一一读完所有的字符。
这样,运算数栈中最后剩下的元素就是该算术表达式的最终结果。
注意:
整个算法结束的条件是运算符栈为空。
(即表达式的起始、结束符相遇)
任务定义:
为统一算法的描述,将运算符和界限符统称为算符。
这样,算符集为+,-,*,/,(,),#}。
根据上述3条运算规则,两个前后相继出现的算符a1、a2间的优先关系可以归纳如下:
(1)若a1、a2同为“*”、“/”或同为“+”、“-”,则运算符a1的优先级大于a2。
(2)“*”、“/”的优先级大于“+”、“-”。
(3)由于“先括号内,后括号外”,若a1为“+”、“-”、“*”、“/”,a2为“(”;或者,a1为“(”,而a2为“+”、“-”、“*”、“/”,则a1的优先级小于a2。
(4)同理,若a1为“+”、“-”、“*”、“/”,a2为“)”;或者,a1为“)”,而a2为“+”、“-”、“*”、“/”,则a1的优先级小于a2。
(5)若a1,a2同为“(”,则a1的优先级小于a2;若a1,a2同为“)”,则a1的优先级大于a2。
(6)表达式的起始、结束符“#”的优先级小于其他所有合法出现的算符。
(7)若a1为“(”,a2为“)”或a1,a2同为“#”,则a1,a2的优先级相同。
表1:
算符a1和a2间的关系
测试用例:
输入的运算表达式为:
#9*(18+2)+10#
输出格式:
显示每一步操作,并且同时显示操作后的序列和栈的变化过程,最后显示正确的结果。
二、概要设计和数据结构的选择
1、数据结构的选择
因为一个算术表达式就是由运算符和运算数两部分构成的,表达式的求值过程就是对这两部分进行操作,并且由运算符的具体操作来控制运算数。
所以为了方便操作,首先可以分别设置一个运算符栈OPTR和一个运算数栈OPND,然后,依次读入表达式中的每个字符。
若是操作数则进运算数栈OPND,反之,则与栈OPTR的栈顶算符进行优先级比较,并作如下处理:
1、若栈顶算符的优先级大于读入的算符,则让该算符入算符栈OPTR;
2、若栈顶算符的优先级高于刚读入的算符,则将栈顶算符出栈,同时,将运算数栈OPND出栈两次,得到两个操作数x,y,对x,y用栈顶算符运算后,再将运算结果入运算数栈OPND;
3、若栈顶算符和读入算符的优先级相等,说明左右括号相遇,或者是表达式的起始、结束符相遇。
只需将OPTR退栈即可。
运算符栈和运算数栈的定义:
typedefstruct//创建运算符栈的类型
{chardata[maxlen];
inttop;
}stack1;
typedefstruct//创建运算数栈的类型
{intdata[maxlen];
inttop;
}stack2;
2、概要设计
程序中实现各个功能的函数有:
1、初始化运算符类型的栈:
voidinitstack1(stack1*s);
2、初始化运算数类型的栈:
voidinitstack2(stack2*s);
3、入运算符栈:
voidpushfu(stack1*s,charc);
4、入运算数栈:
voidpushshu(stack2*L,intp);
5、出运算符栈:
voidpopfu(stack1*s,char*p);
6、出运算数栈:
voidpopshu(stack2*s,int*p);
7、得到运算符栈的栈顶元素:
chargettop1(stack1*s);
8、/得到运算数栈的栈顶元素:
intgettop2(stack2*s);
9、输出运算符栈里的所有算符:
voidvisitoptr(stack1*c);
10、输出运算数栈里的所有算数:
voidvisitopnd(stack2*c);
11、判断字符c是不是运算符:
intjudge(charc);
12、判断优先级:
charyouxianji(charch1,charch2);
13、进行运算函数:
intopreate(inta,charfu,intb);
主函数实现的流程图如下:
否
=
图1:
主函数的流程
三、详细设计和编码
1、初始化运算符类型的栈思想:
申请一个运算符栈类型的指针变量后,将它的top置为-1,即使它为空,然后返回该指针。
voidinitstack1(stack1*s)
{s=(stack1*)malloc(sizeof(stack1));
s->top=-1;
}
2、初始化运算数类型的栈思想:
申请一个运算数栈类型的指针变量后,将它的top置为-1,即使它为空,然后返回该指针。
voidinitstack2(stack2*s)
{s=(stack2*)malloc(sizeof(stack2));
s->top=-1;
}
3、入运算符栈思想:
如果要入的栈不满,就使它的top加1,然后将要插入的算符赋值给top指向的data域中。
voidpushfu(stack1*s,charc)
{if(s->top==maxlen-1)
{cout<<"栈已满,输入错误!
"< else {(s->top)++; s->data[s->top]=c; }} 4、入运算数栈思想: 如果要入的栈不满,就使它的top加1,然后将要插入的算符赋值给top指向的data域中。 voidpushshu(stack2*L,intp) {if(L->top==maxlen-1) {cout<<"栈已满,输入错误! "< else {(L->top)++; L->data[L->top]=p;}} 5、出运算符栈思想: 先将该栈的栈顶元素赋值给同类型的形参变量,然后使得top减1。 voidpopfu(stack1*s,char*p) {*p=s->data[s->top]; (s->top)--; } 6、出运算数栈思想: 先将该栈的栈顶元素赋值给同类型的形参变量,然后使得top减1。 voidpopshu(stack2*s,int*p) {*p=s->data[s->top]; (s->top)--;} 7、得到运算符栈的栈顶元素 chargettop1(stack1*s) {returns->data[s->top];} 8、得到运算数栈的栈顶元素 intgettop2(stack2*s) {returns->data[s->top];} 9、输出运算符栈里的所有算符 voidvisitoptr(stack1*c) {inti; {for(i=0;i<=c->top;i++) cout< cout<<"\t\t";} 10、输出运算数栈里的所有算数 voidvisitopnd(stack2*c) {inti; {for(i=0;i<=c->top;i++) cout< cout< 11、判断字符c是不是运算符: 思想是如果该字符等于“+”,“-”,“*”, “/”,“(”,“)”,“#”中的一个就是运算符,并返回确认1,如果不是,就返回0。 intjudge(charc) {if((c=='+')||(c=='-')||(c=='*')||(c=='/')||(c=='(')||(c=='#')||(c==')')) return1; elsereturn0;} 12、判断优先级: 思想见上面任务定义部分。 charyouxianji(charch1,charch2) {charch; switch(ch1)//将ch1和ch2相比较 {case'+': if((ch2=='*')||(ch2=='/')||(ch2=='('))ch='<';elsech='>';break; case'-': if((ch2=='*')||(ch2=='/')||(ch2=='('))ch='<';elsech='>';break; case'*': if(ch2=='(')ch='<';elsech='>';break; case'/': if(ch2=='(')ch='<';elsech='>';break; case'(': if(ch2==')')ch='=';elsech='<';break; case'#': if(ch2=='#')ch='=';elsech='<';break;} returnch;} 13、进行运算函数: intopreate(inta,charfu,intb) {ints; switch(fu) {case'+': s=a+b;break; case'-': s=a-b;break; case'*': s=a*b;break; case'/': s=a/b;break;} returns;} 四、上机调试 1、调试过程中的问题: (1)、在调试过程中,发生了许多小细节上的问题,提醒了我在编程的时候要注意细节,比即使是一个括号的遗漏或者一个字符的误写都会造成大量的错误,浪费许多时间,总结的教训是要仔细。 (2)、第一次调试时,错误为出栈时不能得到栈顶的值,导致了后面的运算操作不正确,只是地址无算数,后检查发现,在出栈时,定义的函数形式为voidpopshu(stack2*s,intp)更改为voidpopshu(stack2*s,int*p)。 (3)、当编译无误时,开始运行程序,但却出现了内存不能read的错误,但是在进行栈的初始化时已经申请了内存空间,所以不是没有申请空间。 后来上网查阅资料知道也许时初始化栈时使用了无效的指针的错误,所以将初始化运算符栈的函数更改为: stack1*initstack1() {stack1*s; s=(stack1*)malloc(sizeof(stack1)); s->top=-1; return(s);} 将初始化运算数栈的函数更改为: stack2*initstack2(){stack2*s; s=(stack2*)malloc(sizeof(stack2)); s->top=-1; return(s);} 这时程序才能正常的读写内存。 (4)、在测试程序时,发现如果输入了一个不是个位数的数时,程序的运行结果错误。 经检查发现在读入字符时,一个大于个位数的数是分开来从左向右一个一个的输入的,所以在主函数中读入数时要定义一个开关变量,来判别读入的数下一个字符是否还是一个数,如果是就将该数乘以10并赋值给总量后继续读入下个数;如果不是就将得到的总量直接放入运算数栈中并读入一个运算符。 2、算法性能分析 (1).算法时间性能分析 对于算术符优先级比较算法,首先将每个待入栈的运算符判断,这样若有n个算术符,则要判断n次,而接下来又需对栈顶运算符比较,这样,又可分为两种情况,a.栈顶元素优先级高于待入栈运算符,b.栈顶元素优先级低于待入栈运算符,共需判断2n次,而在判断前以判断了n次,所以,这一种情况的比较次数就为2n^2次,像这样的情况共有5种,综合时间复杂度为T(n)=O(5*2*n^2)=O(10*n^2) 对于表达式格式判定算法,因比较复杂,外层为3种情况,而其中一种又有5种情况,这样若有n个字符,则总的判断次数为: n*n*(3*n^2+n+n^3),所以时间复杂度T(n)=O(n^5+3n^4+n^4) (2).空间性能分析 一个算术表达式输入时,存储在一维数组里,对于求表达式算法,一个表达式最少占有空间1(只有一个数子的情况),最多占有空间2n-1(n-1个位置全部有符号),平均一下,平均占空间是3n/2,所以该算法空间复杂度是: S(n)=(3n/2)*4n-1,取f(n)=n*22n,limS(n)/f(n)=3/8。 所以空间复杂度为: O(n*22n)。 对于表达式求值出入栈算法,若该表达式共有n个字符,而共有运算符栈和运算数栈,所以平均为n^2/4,这样,空间复杂度为: O(n*n^2/4)。 五、测试结果与分析 (1)、输入#7+8#,结果显示如下所示: 图2: 算式#7+8#运行截屏 结果分析: 经过亲自将结果运算式计算一遍,证明程序运算正确。 控制算术表达式结束的条件就是“gettop2(OPTR)==’#’”。 (2)、输入: #9*(18+2)/(12-2)#,结果显示如下所示: 图3: 算式#9*(18+2)/(12-2)#运行截屏 结果分析: 该表达式输入了十位数,使得程序的功能得到充分实现。 六、用户使用说明 1.打开“Debug”文件夹,运行“算术表达式求值问题.exe”。 2.用户输入想要求值的算术表达式并以“#”结束。 3.程序会自动显示每步操作并同时显示两个栈的变化过程,最终输出正确的结果。 4.用户选择“Y”或者“y”继续输入表达式求值;选择“N”或者“n”时,按“Enter”键就会直接退出。 七、参考文献 [1]王昆仑,数据结构与算法,北京: 中国铁道出版社,2007 [2]谭浩强,c程序设计,北京: 清华大学出版社,2005 [3]郑莉、董渊、张瑞丰,C++语言程序设计,北京: 清华大学出版社,2003 [4]严蔚敏,数据结构: c语言版,北京: 清华大学出版社,2002 [5]胡学钢,数据结构与算法设计指导,北京: 清华大学出版社,1999 [6]徐士良,C常用算法程序集,北京: 清华大学出版社,1995 [7]关治、陈景良,数值计算,北京: 清华大学出版社,1993 八、附录: 源程序 #include #include #definemaxlen100 usingnamespacestd; intn=0;//定义一个全局变量n并赋值为0 typedefstruct//创建运算符栈的类型 {chardata[maxlen]; inttop; }stack1; typedefstruct//创建运算数栈的类型 {intdata[maxlen]; inttop; }stack2; intjudge(charc)//判断字符c是不是运算符 {if((c=='+')||(c=='-')||(c=='*')||(c=='/')||(c=='(')||(c=='#')||(c==')')) return1; elsereturn0; } stack1*initstack1()//初始化运算符类型的栈 {stack1*s; s=(stack1*)malloc(sizeof(stack1)); s->top=-1; return(s); } stack2*initstack2()//初始化运算数类型的栈 {stack2*s; s=(stack2*)malloc(sizeof(stack2)); s->top=-1; return(s); } voidpushfu(stack1*s,charc)//入运算符栈 {if(s->top==maxlen-1) {cout<<"栈已满,输入错误! "< else {(s->top)++; s->data[s->top]=c; } } voidpushshu(stack2*L,intp)//入运算数栈 {if(L->top==maxlen-1) {cout<<"栈已满,输入错误! "< else {(L->top)++; L->data[L->top]=p;} } voidpopfu(stack1*s,char*p)//出运算符栈 {*p=s->data[s->top]; (s->top)--; } voidpopshu(stack2*s,int*p)//出运算数栈 {*p=s->data[s->top]; (s->top)--; } charyouxianji(charch1,charch2)//判断优先级 {charch; switch(ch1)//将ch1和ch2相比较 {case'+': if((ch2=='*')||(ch2=='/')||(ch2=='('))ch='<';elsech='>';break; case'-': if((ch2=='*')||(ch2=='/')||(ch2=='('))ch='<';elsech='>';break; case'*': if(ch2=='(')ch='<';elsech='>';break; case'/': if(ch2=='(')ch='<';elsech='>';break; case'(': if(ch2==')')ch='=';elsech='<';break; case'#': if(ch2=='#')ch='=';elsech='<';break; } returnch; } chargettop1(stack1*s)//得到运算符栈的栈顶元素 {returns->data[s->top]; } intgettop2(stack2*s)//得到运算数栈的栈顶元素 {returns->data[s->top]; } intopreate(inta,charfu,intb)//进行运算函数 {ints; switch(fu) {case'+': s=a+b;break; case'-': s=a-b;break; case'*': s=a*b;break; case'/': s=a/b;break; } returns; } voidvisitoptr(stack1*c)//输出运算符栈里的所有算符 {inti; {for(i=0;i<=c->top;i++) cout< cout<<"\t\t"; } voidvisitopnd(stack2*c)//输出运算数栈里的所有算数 {inti; {for(i=0;i<=c->top;i++) cout< cout< } voidprintstep()//输出步骤编号 { n++; cout< } voidmain() { charc,t,x,jixu; inti=0,sum=0; intk=1,j=1;//设置了开关变量 inta,b; stack1*OPTR;//定义运算符栈OPTR stack2*OPND;//定义运算数栈OPND OPTR=initstack1();//初始化运算符栈 pushfu(OPTR,'#');//#号压入栈OPTR OPND=initstack2();//初始化运算数栈 for(;;) { cout<<"请输入算术表达式并以#号结束(形如3*(4+5)#): "< cout<<"#"; cin>>c; cout<<"*******************表达式求值演示*****************"< cout<<"步骤"<<"\t\t"; cout<<"操作\t\t"; cout<<"运算符栈"<<"\t"; cout<<"运算数栈"< printstep();//第一步是将#号入栈OPTR cout<<"#号入运算符栈"<<"\t\t"; visitoptr(OPTR); cout<<"\t\t"< while(c! ='#'||gettop1(OPTR)! ='#')//当c是#或者运算符栈栈顶是#时结束操作,输出结果 { if(judge(c)==0)//如果输入的字符是数字,就将其入运算数栈 { sum=0; while(judge(c)==0) { if(! j)//如果开关变量j不为1时,说明输入的不是个位数 { sum=c-'0'; } else sum=sum*10+(c-'0'); cin>>c; } pushshu(OPND,sum);//将得到的数入运算数栈 printstep();//输出步骤,操作,以及运算符栈、运算数栈的变化 cout<<"输入"< visitoptr(OPTR); visitopnd(OPND); j=1; } else if(k) { switch(youxianji(gettop1(OPTR),c))//如果得到的数据是运算符,就将它和运算符栈的栈顶元素比较优先级 { case'<': pushfu(OPTR,c);//如果c的优先级小于栈顶运算符,就将c入运算符栈并继续输入 printstep(); cout<
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算术 表达式 求值 问题 课程设计 报告