用两种方式实现表达式自动计算
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一、设计思想
本项目用两种方式实现表达式自动计算,以下是两种算法的设计思想:
算法一:中缀表达式转后缀表达式并计算
先将中缀表达式转化为后缀表达式,再通过计算后缀表达式求表达式的值。
这种算法要求将表达式扫描两编,第一遍扫描中缀表达式,将其转换为后缀表达式,在这步中需要用到一个运算符栈和一个数组实现。
当扫描表达式时,因为输入表达式后程序得到的是一串字符,所以需要将数字串转化为完整的运算数,将这个运算数按原来的顺序进入数组中;当扫到一个运算符时,如果栈空:运算符直接入栈。
栈不空时:遇到左括号,直接入栈;遇到右括号,则开始出栈放入数组,直到栈顶为左括号,左括号出栈;遇到加、减、乘、除、求余运算符号时,当栈顶的运算符优先级低于扫到的运算符,运算符入栈,否则运算符出栈挂到列表后,直到满足入栈条件;当扫描完整个中缀表达式,将栈里面的运算符全都出栈挂列表后。
另外,因为后缀表达式中两个运算数会连在一起,所以为了区分两个运算数,每当扫描到加、减、乘、除、求余运算符号和栈中运算符出栈挂表后,在列表中添加一个空格作为分隔符。
这样列表中的表达式就是对应的后缀表达式,然后按照要求打印后缀表达式。
第二遍扫描后缀表达式,并计算。
这步中需要用到一个栈来存放扫描到的数。
当扫到一个数时要判断这个数的位数,将这个完整的运算数字符串整个取出,然后把这个字符串转化为数值型(如利用 atof()函数将字符串转换为浮点型等),将其入栈;当扫到一个运算符时,则出栈两个数,先出栈的数作为第一个运算数后出栈的作为第二个运算数,按照所扫描到的运算符进行计算,并将计算结果放到栈中。
当扫描完整个后缀表达式后,在栈中只有一个数,这个数就是我们所要求值的表达式的结果。
算法二:表达式直接求值
用两个栈(一个字符栈、一个数字栈)边扫描边进行计算,扫描完后在数字栈中所得的数就是计算结果。
当扫描到一个数时,因为运算数的位数不一定而且还有小数点,所以在扫到一个数时要判断,如果是操作数和’.’,则将字符型的操作数转换为浮点型的数后存入操作数栈,将这个完整的运算数字符串整个取出,把这个字符串转化为数值型将其入数字栈。
当扫描到一个运算符时,如果运算符栈空:运算符直接入运算符栈。
运算符栈不空时:遇到左括号,直接入运算符栈;遇到右括号则从数字栈中出栈两个数,先出栈的数作为第一个运算数后出栈的作为第二个运算数,从运算符栈中出栈一个运算符,将两个数按照出栈运算符进行计算,并将计算结果放到数字栈中,直到运算符栈栈顶为左括号,左括号出栈;遇到加、减、乘、除、求余运算符号时,当运算符栈栈顶的运算符优先级低于扫到的运算符,运算符入运算符栈,否则从数字栈中出栈两个数,先出栈的数作为第一个运算数后出栈的作为第二个运算数,从运算符栈中出栈一个运算符,将两个数按照出栈运算符进行计算,并将计算结果放到数字栈中,直到满足入栈条件。
当扫描完整个表达式,如果运算符栈中不空,则从数字栈中出栈两个数,先出栈的数作为第一个运算数后出栈的作为第二个运算数,从运算符栈中出栈一个运算符,将两个数按照出栈运算符进行计算,并将计算结果放到数字栈中,直到运算符栈为空。
这样,最后在数字栈中只有一个数了,这个数就是我们所要求值的表达式的结果。
二、算法流程图
这部分要画出每个算法的流程图,并对每个流程图用文字简单说明。
文字用5号字,宋体。
文档中的所有图片要设置为居中,图下面要有图号和图名,图号和图名小5字,居中。
如下所示。
图1 xxxx算法流程图
三、源代码
这部分主要是给出两种算法程序的源代码,每段源代码前要用文字简短说明。
文字用5号字,宋体。
源代码用小5号字,Times New Roman字体,如下所示
下面给出的是用扫两遍算法实现的程序的源代码:在本算法中在每次扫描表达式时各用到一个栈,在第一边扫描过程中先把中缀表达式转换为后缀表达式,通过计算后缀表达式,间接的求得表达式的值。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#define STACK_SIZE 100
typedef struct /* 定义运算符栈*/
{
char * base; /*栈底指针*/
char * top; /*栈顶指针*/
}OPTR_Stack;
typedef struct /* 定义操作符栈*/
{
float * base; /*栈底指针*/
float * top; /*栈顶指针*/
}OPND_Stack;
void RInIt(OPTR_Stack *s) /* 初始化运算符栈*/
{
s->base=(char*)malloc(STACK_SIZE*sizeof(char));
/*分配内存,转化首地址为SelemType*型*/
if(s->base==NULL) exit(0); /* 判断栈是否为空*/
s->top=s->base; /* 初始化栈*/
}
int REmpty(OPTR_Stack s) /* 定义栈空事件*/
{
if(s.top==s.base) /*判断栈空*/
return 1; /* 栈空返回1,否则为0 */ else
return 0;
}
void RPush(OPTR_Stack *s,char elem) /* 入栈操作符*/ {
if (s->top-s->base==STACK_SIZE) /* 判断栈满*/
{
printf("OPTR_Stack is full!\n"); /*栈满则打印*/
getch(); /*停止*/
exit(0);
}
else
*(s->top++)=elem; /*栈未满则elem入栈*/ }
char RPop(OPTR_Stack *s) /* 出栈*/
{
char elem; /*定义字符变量*/
if(REmpty(*s)) /*判断栈是否为空*/
{
printf("Stack is empty\n"); /*栈空打印*/
getch(); /*停止*/
exit(0); /*返回0*/
}
else /*栈不为空*/
{
elem=*--s->top; /*出栈值传给elem*/
return elem; /*返回值*/
}
}
char RGetTop(OPTR_Stack s) /* 获取栈顶元素*/
{
char elem; /*定义字符变量*/
if(!REmpty(s)) /*判断栈s是否为空*/
{
elem=*(s.top-1); /*将栈顶元素值传给elem*/
return elem; /*返回值*/
}
}
void DInIt(OPND_Stack *s) /* 初始化数栈*/
{
s->base=(float *)malloc(STACK_SIZE*sizeof(float));/*分配内存,转化首地址为SelemType*型*/
if(s->base==NULL) exit(0); /* 判断栈是否为空*/
s->top=s->base; /* 初始化栈*/
}
int DEmpty(OPND_Stack s) /* 判断数栈是否为空*/
{
if(s.top==s.base)
return 1; /*栈为空则返回1*/
else
return 0; /*栈不为空则返回0*/
}
void DPush(OPND_Stack *s,float elem) /* 入栈*/
{
if (s->top-s->base==STACK_SIZE) /* 判断栈满*/
{
printf("OPND_Stack is full!\n"); /*栈满则打印*/
getch(); /*停止*/
exit(0);
}
else
*s->top++=elem; /*栈未满则elem入栈*/
}
float DPop(OPND_Stack *s) /* 出栈*/
{
float elem; /*定义字符变量*/
if(DEmpty(*s)) /*判断栈s是否为空*/
{
printf("OPND_Stack is empty\n"); /*栈空则打印*/
getch(); /*停止*/
exit(0);
}
else
{
elem=*--s->top; /*栈非空出栈*/
return elem;
}
}
float DGetTop(OPND_Stack s) /* 获取栈顶元素*/
{
float elem; /*定义字符变量*/
if(!DEmpty(s)) /*判断栈s是否为空*/
{
elem=*(s.top-1); /* 将栈顶值传给elem*/
return elem; /*返回值*/
}
}
int isoper(char c) /* 判断是否为运算符*/
{
if(c=='+'||c=='-'||c=='*'||c=='/'||c=='%')
return 1; /*如果c是'+''-''/''*'中的一种则返回1*/ else
return 0; /*否则返回0*/
}
int priority(char c) /* 判断优先级*/
{
int p; /*定义一个整型数*/
switch(c)
{
case '+':
case '-': p=1;break; /* +-的优先级为1*/
case '*':
case '%':
case '/': p=2;break; /* '*''%''/'的优先级为2*/
case '(': p=0;break; /* (的优先级为0*/
}
return(p); /*返回值*/
}
float calculate(float s1,float s2,char oper) /* 计算表达式*/
{
float y; /*定义一个浮点型*/
switch(oper)
{
case '+': y=s2+s1;break; /*情形为+时定义y为两值和*/
case '-': y=s2-s1;break; /*情形为-时定义y为两值差*/
case '*': y=s2*s1;break; /*情形为*时定义y为两值积*/
case '%': y=(int)((int)s2%(int)s1); break; /*情形为+时定义y为两值求余*/
case '/': /*情形为‘/’时*/
if(s1==0) /*当除数为0时*/
{
printf("\nDevided by zero!"); /*打印除数为0*/
getch(); /*停止*/
exit(0);
}
y=s2/s1;break; /*除数不为0则定义y值为两数商*/ }
return(y); /*返回值*/
}
float StrToFloat(char *s,int *j) /* 将字符串转化成数字*/
{
int i,k; /*定义两个整型变量*/
char n[50]; /*定义一个数组*/
float f; /*定义一个浮点型*/
i=*j;
for(k=0;s[i]>='0' && s[i]<='9' || s[i]=='.';i++,k++) /*当数组里为0~9的数值或是小数点*/ {
n[k]=s[i]; /*将字符存入数组*/
}
n[k]='\0'; /*存入一个数值后数值间加空格*/
*j=i;
f=atof(n); /*将字符数组转换成浮点型数据*/
return(f); /*返回值*/
}
int check(char exp1[STACK_SIZE]) /*对于输入表达式的错误的判定*/
{
int i=0; /*定义整型数*/
while(exp1[i]!='\0') /*数组结束则结束循环*/
{
if(exp1[i]=='+'||exp1[i]=='-'||exp1[i]=='('||exp1[i]==')'||exp1[i]=='*'
||exp1[i]=='/'||exp1[i]=='%'||exp1[i]=='.'||exp1[i]>='0'
&&exp1[i]<='9')
{ }
else /*不为上述字符时*/
return 1; /*返回1*/
i++;
}
if(exp1[i]=='\0') /*数组结束*/
return 0; /*返回0*/
}
void main()
{
char exp1[100]; /*表达式*/
char oper;
int i,k=0,j=0;
int p[100]; /*后缀表达式中运算符位置*/
float OPND1,OPND2,result,f;
float exp2[100]; /*后缀表达式*/
OPTR_Stack OPTR; /* 运算符栈OPTR*/
OPND_Stack OPND; /*操作数栈OPND */
RInIt(&OPTR); /*初始化符号栈*/
DInIt(&OPND); /*初始化操作数栈*/
printf("Please input the inorder expression:");
gets(exp1); /*输入值放入数组exp1中*/
printf("\nThe expression is:");
puts(exp1); /*输出输入的表达式*/
if(check(exp1)) /*对于输入表达式的错误的判定*/
{
printf("\n\t error"); /*表达式的错误则返回error*/
getch(); /*停止*/
}
for(i=0;exp1[i]!='\0' && exp1[i]!='\n';i++) /*中缀转后缀*/
{
if(exp1[i]>='0' && exp1[i]<='9' || exp1[i]=='.') /* 将字符型的操作数转化为数字,存入存入后缀表达式exp2中*/
{
f=StrToFloat(exp1,&i); /*将数组exp1中的字符型操作数转换为浮点型的数*/
exp2[k]=f;k++; /*将转换后的数存入数组exp2中*/ }
if(isoper(exp1[i])) /* 为运算符时*/
{
if(!REmpty(OPTR)) /*当符号栈不为空时*/
{
while(!REmpty(OPTR) && priority(exp1[i])<=priority(RGetTop(OPTR)))
/* 比较优先级,栈顶运算符优先级高时,运算符出栈*/
{
oper=RPop(&OPTR); /*出栈将值传给oper*/
exp2[k]=(float)oper; /*将出栈的值放入后缀表达式中*/
p[j]=k;k++;j++; /*记录出栈符号在数组中的位置*/
}
RPush(&OPTR,exp1[i]); /*栈顶运算符优先级低时,运算符入栈*/ }
else
RPush(&OPTR,exp1[i]); /*符号栈为空时,运算符入栈*/
}
else if(exp1[i]=='(') RPush(&OPTR,exp1[i]); /*'('入栈*/
else if(exp1[i]==')')
{
while(RGetTop(OPTR)!='(' && !REmpty(OPTR)) /*运算符出栈,直到遇到')'或者栈空*/
{
oper=RPop(&OPTR); /*出栈将值传给oper*/
exp2[k]=(float)oper; /*将出栈的值放入后缀表达式中*/
p[j]=k; /*记录出栈符号在数组中的位置*/
j++;k++;
}
RPop(&OPTR); /*将栈内的'('丢掉*/
}
}
while(!REmpty(OPTR)) /* 将栈中的剩余的运算符存入表达式exp2中*/
{
oper=RPop(&OPTR); /*出栈将值传给oper*/
exp2[k]=(float)oper; /*将出栈的值放入后缀表达式中*/
p[j]=k; /*记录出栈符号在数组中的位置*/
j++;k++;
}
printf("\nThe surffix expression is:\n\n");
for(k=0,j=0;(char)exp2[k]!='\0';k++) /*输出并且计算后缀表达式*/
{
if(k==p[j]) /*当后缀表达式中为符号所在位置时*/
{
printf("%5c",(char)exp2[k]); /*打印符号*/
OPND1=DPop(&OPND); /*操作数栈中出栈一个数*/
OPND2=DPop(&OPND); /*操作数栈中再出栈一个数*/
result=calculate(OPND1,OPND2,(char)exp2[k]); /*将出栈的数和符号进行计算*/
DPush(&OPND,result); /*将计算的结果入栈*/
j++;
}
else
{
printf("%10f",exp2[k]); /*当后缀表达式中数值所在位置时,打印操作数*/
DPush(&OPND,exp2[k]); /*数值入栈*/
}
}
printf("\n\nThe result is:%f",result); /*打印计算结果*/
getch();
}
四、运行结果
这部分主要是将程序运行抓取的运行结果图片及说明。
文档中的所有图片要设置为居中,图下面要有图号和图名,图号和图名小5字,居中。
如下所示。
图5 xxxx运行结果图
注意图片编号要与前面的统一,前面到图2这里就是图3。
另外注意抓图时命令行窗口中用白底黑字。
五、遇到的问题及解决
这部分我主要遇到了如下两个问题,其内容与解决方法如下所列:
●对于输入表达式的错误的判定。
对于输入的表达式,当输入的表达式不符合程序的规定(如输入¥,@,字母等非法字符)时,程序不能正确得出结果。
为了保持程序的完整性,需要对输入表达式进行判错。
当输入错误时,程序应该提示出现了错误。
为了保证输入的字符为规定的操作符或操作数,要对输入的表达式进行扫描、判断。
一开始算法是如果符合要求就返回0,不符合要求返回1,但是这样一旦出现问题,循环就无法进行下去。
所以,经过反复考虑,最终设定为当符合要求时不做任何操作,不符合要求时返回1。
当扫描完后发现都符合时,返回0。
这样,问题得以解决。
●后缀表达式。
注意:问题至少两个,“遇到的问题及解决”部分的内容不得少于0.7页纸
六、心得体会
这部分主要是给出在做完本项目后的一些收获和体会,文字用5号字,宋体。
不得少于350字。