算术表达式求值问题课程设计报告.docx

上传人:b****1 文档编号:2675598 上传时间:2023-05-04 格式:DOCX 页数:23 大小:144.46KB
下载 相关 举报
算术表达式求值问题课程设计报告.docx_第1页
第1页 / 共23页
算术表达式求值问题课程设计报告.docx_第2页
第2页 / 共23页
算术表达式求值问题课程设计报告.docx_第3页
第3页 / 共23页
算术表达式求值问题课程设计报告.docx_第4页
第4页 / 共23页
算术表达式求值问题课程设计报告.docx_第5页
第5页 / 共23页
算术表达式求值问题课程设计报告.docx_第6页
第6页 / 共23页
算术表达式求值问题课程设计报告.docx_第7页
第7页 / 共23页
算术表达式求值问题课程设计报告.docx_第8页
第8页 / 共23页
算术表达式求值问题课程设计报告.docx_第9页
第9页 / 共23页
算术表达式求值问题课程设计报告.docx_第10页
第10页 / 共23页
算术表达式求值问题课程设计报告.docx_第11页
第11页 / 共23页
算术表达式求值问题课程设计报告.docx_第12页
第12页 / 共23页
算术表达式求值问题课程设计报告.docx_第13页
第13页 / 共23页
算术表达式求值问题课程设计报告.docx_第14页
第14页 / 共23页
算术表达式求值问题课程设计报告.docx_第15页
第15页 / 共23页
算术表达式求值问题课程设计报告.docx_第16页
第16页 / 共23页
算术表达式求值问题课程设计报告.docx_第17页
第17页 / 共23页
算术表达式求值问题课程设计报告.docx_第18页
第18页 / 共23页
算术表达式求值问题课程设计报告.docx_第19页
第19页 / 共23页
算术表达式求值问题课程设计报告.docx_第20页
第20页 / 共23页
亲,该文档总共23页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

算术表达式求值问题课程设计报告.docx

《算术表达式求值问题课程设计报告.docx》由会员分享,可在线阅读,更多相关《算术表达式求值问题课程设计报告.docx(23页珍藏版)》请在冰点文库上搜索。

算术表达式求值问题课程设计报告.docx

算术表达式求值问题课程设计报告

合肥学院

算术表达式求值演示

一、问题分析和任务定义

实验题目:

算术表达式求值:

一个算术表达式是由操作数(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<data[i];}

cout<<"\t\t";}

10、输出运算数栈里的所有算数

voidvisitopnd(stack2*c)

{inti;

{for(i=0;i<=c->top;i++)

cout<data[i]<<",";}

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<data[i];}

cout<<"\t\t";

}

voidvisitopnd(stack2*c)//输出运算数栈里的所有算数

{inti;

{for(i=0;i<=c->top;i++)

cout<data[i]<<",";}

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<

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 幼儿教育 > 育儿知识

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2