数据结构课程设计用两种方式实现表达式自动计算.docx
- 文档编号:4561150
- 上传时间:2022-12-06
- 格式:DOCX
- 页数:20
- 大小:128.74KB
数据结构课程设计用两种方式实现表达式自动计算.docx
《数据结构课程设计用两种方式实现表达式自动计算.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计用两种方式实现表达式自动计算.docx(20页珍藏版)》请在冰豆网上搜索。
数据结构课程设计用两种方式实现表达式自动计算
一、设计思想
(一)中缀转后缀的设计思想
设计一个能实现表达式自动求值计算,算术表达式由操作数、算符和括号组成。
由于运算符的优先级不同还要考虑括号。
所以表达式不可能一直的从左到右进行,所以就借助栈来实现这个表达式的求值。
首先要把算术表达式变换成与之等值的无括号表达式,也就是中缀转后缀,它也是这个算法的关键。
设计两个栈,一个为字符型的,存放运算符,用以将算术表达式变成无括号的表达式;另一个浮点型的,存放操作数,用以对无符号的表达式进行求值。
我们要假设运算符的优先级:
(),*/,+-。
首先将一左括号‘(’入栈,作为栈底元素;接着从左到右对算术表达式进行扫描。
每次读一位,若遇到左括号‘(’,则进栈;若遇到的是操作数,则立即输出;若又遇到运算符,如果它的优先级比栈顶元素的优先级数高的话,则直接进栈,否则输出栈顶元素,直到新的栈顶元素的优先级数比它低的,然后将它压栈;若遇到是右括号‘)’,则将栈顶的运算符输出,直到栈顶的元素为‘(’,然后,左右括号互相底消;到设计的结束标志的时候表示表达式已经扫描完毕,表达式已经全部输入,将栈中的运算符全部输出,删除栈底的左括号。
以上完成了中缀表达式转后缀表达式,输出无括号的后缀表达式。
读后缀表达式,若遇数值,操作数进栈;若遇运算符,让操作数栈的栈顶和次栈顶依次出栈并与此运算符进行运算,运算结果入操作数栈;重复这个步骤,直到遇到结束标志,则此时栈中的结果便是所求的后缀表达式的值,接着输出结果。
以上就是设计这个算法的主要的思想。
(二)直接计算的设计思想
直接计算其实跟上一个相似,它是在上面扫两遍的思想进行修改的得来。
首先,要建立两个栈,一个为字符型的,存放运算符,另一个浮点型的,存放操作数,我们开始对表达式进行扫描。
首先要确定运算符的优先级:
(、)、*、/、+、-。
如果扫描到的是数字符号,把它们转换成浮点型数据或其他可运算的数据类型,存入操作数栈中。
如果扫描到的是运算符号,第一个运算符进栈,遇到‘(’存入运算符栈中,我们按照第一种算法的方法将表达式依次扫描。
只不过不同的是,当每取得的一个运算符的时候,都要与栈顶的运算符进行比较,如果它的优先级小于栈顶运算符优先级时,取出栈顶运算符并从操作数栈中取栈顶两个数进行运算,得到的结果则要存回操作数栈,就这样边扫描边比较,再进行计算。
遇到“)”对运算符的处理相同。
扫描结束后,把运算符栈的元素和操作数栈里的数进行运算。
每次的运算结果再放入操作数栈,一直到计算到运算符栈空。
最后操作数栈的栈顶留下的操作数即表达式的计算结果。
以上就是直接计算表达式的思路。
二、算法流程图
(一)中缀转后缀算法的流程图
图1中缀转后缀算法的流程图
流程图说明:
该流程图分两部分成,第一部分是中缀转后缀部分,第二部分是用于后缀表达式求值的。
(二)直接计算算法的流程图
图2接计算算法的流程图
流程图说明:
该流程图进过图1改进得到,只是缺少中缀转后缀的部分。
三、源代码
下面给出的是用中缀转后缀算法实现的程序的源代码:
#include
#defineN50
typedefstructStackArray{
floatdata[N];
inttop;
}StackArray;/*定义操作数栈*/
typedefstructStackChar{
chardata[N];
inttop;
}StackChar;/*定义操作符栈*/
StackArray*InitNum()/*初始化数值栈*/
{
StackArray*p=(StackArray*)malloc(sizeof(StackArray));/*取一段内存赋予数值栈*/
p->top=-1;
returnp;/*返回*/
}
StackChar*InitChar()/*初始化操作符栈*/
{
StackChar*p=(StackChar*)malloc(sizeof(StackChar));/*取一段内存赋予数值栈*/
p->top=-1;
returnp;
}
intPushNum(StackArray*p,floatvalue)/*定义入栈函数*/
{
if(p->top<50-1)
{
p->top+=1;
p->data[p->top]=value;/*栈顶*/
return1;
}
else
{
return0;
}
}
intPushChar(StackChar*p,charvalue)/*操作符入栈*/
{
if(p->top<50-1)
{
p->top+=1;
p->data[p->top]=value;/*入栈为栈顶*/
return1;
}
else
{
return0;/*返回值*/
}
}
intPopNum(StackArray*p,float*value)/*操作数出栈*/
{
if(p->top>=0)/*判断栈是否为空*/
{
*value=p->data[p->top];
p->top-=1;
}
return1;
}
intPopChar(StackChar*p,char*q)/*操作符出栈函数*/
{
if(p->top>=0)/*定义操作符栈*/
{
*q=p->data[p->top];
p->top-=1;
}
return1;
}
floatVisitNum(StackArray*p)/*定义数值栈遍历函数*/
{
if(p->top!
=-1)
returnp->data[p->top];
elsereturn0;
}
charvisitChar(StackChar*p)/*定义操作符栈遍历函数*/
{
if(p->top!
=-1)
returnp->data[p->top];
elsereturn0;
}
intlevel(charc)/*优先级*/
{
switch(c)
{
case'#':
return0;/*#的优先级*/
case'+':
return1;
case'-':
return1;/*‘-‘的优先级*/
case'*':
return2;
case'/':
return2;
case'(':
return3;/*(优先级*/
default:
return-1;
}
}
intstrLen(char*L)/*字符串长度函数*/
{
inti;
for(i=0;L[i]!
='\0';i++);/*循环条件*/
if(L[0]=='-'||L[0]=='+')
{
i=i-1;
}
returni;
}
floatCompute(floata,charc,floatb)/*计算*/
{
switch(c)
{
case'+':
returna+b;/*加号的运算*/
case'-':
returna-b;
case'*':
returna*b;/*乘号的运算*/
case'/':
returna/b;/*除号的运算*/
default:
return-1;
}
}
voidmain()
{
StackArray*data;
StackChar*op;
inti,j=0;
floatn;
floatresult,opA,opB;
charopChar;
chararray[N];
char*ptarray=NULL;/*定义指针*/
floathouzhui[N];
printf("Pleaseenteradata:
\t");
while((scanf("%s",array))!
=EOF)/*循环条件*/
{
op=InitChar();
data=InitNum();
PushChar(op,'#');
array[strLen(array)]='#';/*结束标志*/
ptarray=array;
for(i=0;i { if(array[i]>=48&&array[i]<=57||array[i]=='.')/*判断数值 { doubleweight=0.1; intflag=0;/*定义变量*/ floatn=0;/*定义变量*/ n=array[i]-48; while(array[i+1]>=48&&array[i+1]<=57||array[i+1]=='.')/*判定数值*/ { if(array[i+1]=='.')/*小数*/ flag=1; else { if(flag==0)n=n*10+array[i+1]-48;/*整数化浮点数*/ else { n=n+(array[i+1]-48)*weight;/*小数化浮点数*/ weight*=0.1;/*小数位权*/ } } i++; } houzhui[j]=n; j++; } else { if(array[i]=='#')/*结束标志*/ { while(visitChar(op)! ='#')/*对操作符栈遍历*/ { PopChar(op,&opChar); houzhui[j]=opChar;/*出栈入数组*/ j++; } } else { if(level(array[i])>level(visitChar(op))|| visitChar(op)=='(')/*判断优先级*/ { PushChar(op,array[i]);/*入栈*/ } else { if(array[i]==')')/*遇见字符‘)’*/ { while(visitChar(op)! ='(')/*出战到‘(’*/ { PopChar(op,&opChar); houzhui[j]=opChar;j++;/*操作符入数组*/ } PopChar(op,&opChar); }else { while(level(array[i])<= level(visitChar(op)))/*优先级*/ { PopChar(op,&opChar); houzhui[j]=opChar;/*出栈*/ j++; } PushChar(op,array[i]);/*运算符进栈*/ } } } } } for(j=0;houzhui[j]! ='\0';j++) { if((char)houzhui[j]=='+'||(char)houzhui[j]=='-'|| (char)houzhui[j]=='*'||(char)houzhui[j]=='/')/*类型转换*/ { printf("%2c",(char)houzhui[j]);/*输出一个运算符*/ PopNum(data,&opA); PopNum(data,&opB); result=Compute(opB,(char)houzhui[j],opA);/*调用函数*/ PushNum(data,result);/*结果入栈*/ } else { PushNum(data,houzhui[j]); printf("%8f",houzhui[j])/*输出后缀表达式*/; } } printf("\ntheResultis: %.2f\n\n",result);/*输出结果*/ } } 下面给出的是用直接计算算法实现的程序的源代码: #include #defineN50 typedefstructlistStack { floatdata[N]; inttop; }listStack;/*定义存储操作数的栈*/ typedefstructopStack { chardata[N]; inttop; }opStack;/*用于定义存储操作符号的栈*/ listStack*InitNum()/*初始化数值栈*/ { listStack*p=(listStack*)malloc(sizeof(listStack));/*取一段内存赋予数值栈*/ p->top=-1;/*定义栈底*/ returnp; } opStack*InitChar()/*初始化操作符栈*/ { opStack*p=(opStack*)malloc(sizeof(opStack));/*取一段内存赋予操作符栈*/ p->top=-1; returnp; } intPushNum(listStack*p,floatvalue)/*定义入栈函数*/ { if(p->top { p->top+=1; p->data[p->top]=value;/*入栈的数值为栈顶元素*/ return1; } else { return0; } } intPopNum(listStack*p,float*a)/*定义数值出栈函数*/ { if(p->top>=0)/*判定栈不为空*/ { *a=p->data[p->top]; p->top-=1; } return1; } floatVisitNum(listStack*p)/*定义数值栈遍历函数*/ { if(p->top! =-1)/*判定栈是否为空*/ returnp->data[p->top]; elsereturn0; } voidPushChar(opStack*p,chara)/*定义操作符入栈函数*/ { if(p->top { p->top+=1; p->data[p->top]=a;/*入栈字符为栈顶元素*/ } else; } intPopChar(opStack*p,char*a)/*定义操作符出栈函数*/ { if(p->top>=0)/*判定栈不为空*/ { *a=p->data[p->top]; p->top-=1; } return1; } charvisitChar(opStack*p)/*定义操作符栈遍历函数*/ { if(p->top! =-1) returnp->data[p->top];/*返回栈顶的值*/ elsereturn0; } intstrLen(char*L)/*计算字符串长度*/ { inti; for(i=0;L[i]! ='\0';i++);/*循环条件的判定*/ if(L[0]=='-'||L[0]=='+') { i=i-1; } returni; } intlevel(charc)/*符号的优先级*/ { switch(c) { case'#': return0; case'+': return1; case'-': return1; case'*': return2; case'/': return2; case'(': return3; default: return-1; } } floatCompute(floata,charch,floatb)/*数据运算*/ { switch(ch) { case'+': returna+b;/*返回加号数值运算*/ case'-': returna-b;/*返回减号数值运算*/ case'*': returna*b; case'/': returna/b;/*返回除号数值运算*/ default: return-1; } } voidmain() { listStack*data;/*定义操作数栈,由指针data指出*/ opStack*op; inti;/*对变量进行声明*/ floatn; floatresult,opA,opB; charoperand[2],opChar;/*定义字符型数组和字符变量*/ chararray[N]; char*ptarray=NULL;/*定义字符型指针*/ printf("Pleaseenteradata: \t"); while((scanf("%s",array))! =EOF)/*判断循环的条件*/ { op=InitChar(); data=InitNum(); PushChar(op,'#'); array[strLen(array)]='#';/*将字符#赋予数组最后*/ ptarray=array; for(i=0;i { if(array[i]>=48&&array[i]<=57|| array[i]=='.')/*判断值*/ { doubleweight=0.1; intflag=0;/*定义整形变量flag*/ floatn=0;/*定义浮点型变量*/ n=array[i]-48; while(array[i+1]>=48&&array[i+1]<=57|| array[i+1]=='.')/*判定数值*/ { if(array[i+1]=='.') flag=1;/*读到小数点*/ else { if(flag==0)n=n*10+array[i+1]-48;/*将整数部分字符串转化为实数*/ else { n=n+(array[i+1]-48)*weight;/*将表示小数部分的字符也转化过来*/ weight*=0.1;/*weight为小数位权*/ } } i++; } PushNum(data,n);/*将数值进栈*/ } else { if(array[i]=='#')/*遇见字符#*/ { while(visitChar(op)! ='#')/*对操作符栈进行遍历*/ { PopChar(op,&opChar);/*字符出栈*/ PopNum(data,&opA); PopNum(data,&opB);/*数出栈*/ result=Compute(opB,opChar,opA);/*调用运算函数*/ PushNum(data,result); } } else { if(level(array[i])>level(visitChar(op))|| visitChar(op)=='(')/*判断操作符的优先级高低*/ { PushChar(op,array[i]);/*操作符进栈*/ } else { if(array[i]==')')/*当操作符为)时*/ { while(visitChar(op)! ='(')/*遍历操作符栈*/ { PopChar(op,&opChar); PopNum(data,&opA); PopNum(data,&opB);/*从数值栈输出一个值*/ result=Compute(opB,opChar,opA); PushNum(data,result);/*结果放回栈里*/ } PopChar(op,&opChar);/*输出操作符*/ } else { while(level(array[i])<=level(visitChar(op)))/*判断操作符的优先级高低*/ { PopChar(op,&opChar); PopNum(data,&opA);/*从数值栈输出数值*/ PopNum(data,&opB); esult=Compute(opB,opChar,opA);/*调用运算函数*/ PushNum(data,result);/*将运算结果入栈*/ } PushChar(op,array[i]);/*将操作符入栈*/ } } } } } printf("theResultis: %.6f\n\n",result);/*输出扫描一遍的运算结果*/ } } 四、运行结果 (一)中缀转后缀算法的运行结果: 图3中缀转后缀算法的运行结果 (二)直接计算算法的运行结果: 图4直接计算算法的运行结果 五、遇到的问题及解决 这部分我主要遇到了如下两个问题,其内容与解决方法如下所列: ●问题1: 在遇到小数点处理的问题上,虽然说处理小数点的方法不是唯一的,但我在处理的时候只能计算一位小数,如果输入的是两位的话,计算结果的时候它就把一位小数后面的数据全丢了。 解决方法: 在输出后缀表达式的时候可以看到栈里存的数据最多只带一位小数,所以结果也只能出现一位小数。 因为在处理小数的时候我是让小数点后面的数乘以0.1,再加上前面的。 但如果是两位的话,那么最后的那一个是要乘以0.01的,也就是乘以0.1的自乘,所以在处理小数时我加了自乘的这行代码,运行的结果才真确的出来了。 ●问题2: 无论输入什么表达式,不知道结果为什么始终是‘0.00‘,运行还是行的,有的时候程序只是按照扫到最后的一个运算符来执行。 图5运行出错 解决方法: 刚开始不知道哪儿出错了,就瞎找那些有可能会出现错的地方,比如: 能从图中直接看出来,没有后缀表达式;计算结果也是错误的;还有可能是哪个函数调用时出了错。 随后就从最简单的函数调用出错的问题开始检查,没有发现什么明显的错误,就放弃了这个问题,接着就是计算有没有出现简单的错误,因为它比较于中缀转后缀要简单一些,就开始试,单独的对这一段代码试,结果还是没发现什么错误,最后就剩下最难的中缀转后缀的问题了,如果这个也没有错,那就不知道哪儿出错了,就开始对它调试,看了两个小时也没找出来什么错误。 以后才找到那儿出了漏洞,原来缺少一句赋值语句“houzhui[j]=n;”就成这样了,这个错误告诉我一点点的小错误也能导致整个程序的错误。 六、心得体会 通过这次的作业我充分的认识到了自己的不足,特别是对写程序代码这方面,一个程序从算法到用程序把它实现出来,这一整个过程是很不容易的,你懂得它的算法,不一定就能写的出来,通过这次我也深深的了这一点。 对于一个新手来说,小的错误出现的太多,而且一个小的错误就能让我束手无策,因为想不通错在哪,所以就一直在乱改,逻辑错误就更难了,有时候程序运行语法没有错误,但只要输入表达
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 课程设计 用两种 方式 实现 表达式 自动 计算