目录
1 需求分析
1.1 任务
输入一个中缀表达式,求解表达式的值。运算符包括“+”、“-”、“*”、“/”、“(”、“)”和“=”,参与运算的数为double类型且为正数。
1.2 输入形式
多组数据,每组数据占一行,对应一个算术表达式,每个表达式均以“=”结尾。当表达式只有一个“=”时,输入结束。参加运算的数为double类型且所有符号均为英文符号。例如:(13.14-5.2)*(5.21-5.2)=
1.3 输出形式
对于每组数据输出一行,为表达式的运算结果。输出保留两位小数。当输入字符串为一个“=”时,输出提示“运行结束,欢迎下次使用!”。
1.4 程序功能
(1)主函数功能:调用各子函数对输入的中缀表达式进行计算并输出计算结果。
(2)“Status InitSqStackList_R(SqStackR *R)”和“Status InitSqStackList_D(SqStackD *D)”用于初始化寄存运算符的运算数的栈空间;
(3)“RElement GetTop_R(SqStackR R)”和“DElement GetTop_D(SqStackD D)”分别用于获取运算符的运算数栈的栈顶元素;
(4)“Status Push_R(SqStackR *R,RElement sign)”和“Status Push_D(SqStackD *D,DElement number)”分别用于运算符的运算数的进栈;
(5)“Status Pop_R(SqStackR *R,RElement *sign)”和“Status Pop_D(SqStackD *D,DElement *number)”分别用于运算符和运算数的出栈;
(6)“char Precede(char a, RElement b)”用于对当前源于字符串的运算符和运算符栈的栈顶元素进行优先级比较,并返回“<”、“=”或“>”;
(7)“double Operate( double d, char e ,double f)”用于计算数据,并返回计算结果。
2 概要设计
2.1 抽象数据类型定义
//栈结构,用于保存运算符
typedef struct {
RElement *base;
RElement *top;
int StackSize;
}SqStackR;
//栈结构,用保存运算数
typedef struct {
DElement *base;
DElement *top;
int StackSize;
}SqStackD;
2.2 主程序流程图
3 详细设计
3.1 功能函数设计
//存放数据和运算符的栈的初始化
Status InitSqStackList_R(SqStackR *R){ //符号顺序栈的初始化
R->base=(RElement*)malloc(Init_StackSize*sizeof(RElement));
if(!R->base) exit(OVERFLOW);
R->top=R->base;
R->StackSize=Init_StackSize;
return OK;
}
Status InitSqStackList_D(SqStackD *D){ //数值顺序栈的初始化
D->base=(DElement*)malloc(Init_StackSize*sizeof(DElement));
if(!D->base) exit(OVERFLOW);
D->top=D->base;
D->StackSize=Init_StackSize;
return OK;
}
//获取栈顶元素
RElement GetTop_R(SqStackR R){ //符号顺序栈-获取栈顶元素
if(R.base==R.top) return ERROR;
return *--R.top;
}
DElement GetTop_D(SqStackD D){ //数值顺序栈-获取栈顶元素
if(D.base==D.top) return ERROR;
return *--D.top;
}
//数值、运算符进栈
Status Push_R(SqStackR *R,RElement sign){ //符号顺序栈-压栈
if(R->top-R->base>=R->StackSize){
R->base=(RElement*)realloc(R->base,(Init_StackSize+StackIncrease)*sizeof(RElement));
if(!R->base) exit(OVERFLOW);
R->top=R->base+R->StackSize;
R->StackSize+=StackIncrease;
}
*R->top++=sign;
return OK;
}
Status Push_D(SqStackD *D,DElement number){ //数值顺序栈-压栈
if(D->top-D->base>=D->StackSize){
D->base=(DElement*)realloc(D->base,(Init_StackSize+StackIncrease)*sizeof(DElement));
if(!D->base) exit(OVERFLOW);
D->top=D->base+D->StackSize;
D->StackSize+=StackIncrease;
}
*D->top++=number;
return OK;
}
//数值、运算符出栈
Status Pop_R(SqStackR *R,RElement *sign){ //符号顺序栈-出栈
if(R->base==R->top) return ERROR;
*sign=*--R->top;
return OK;
}
Status Pop_D(SqStackD *D,DElement *number){ //数值顺序栈-出栈
if(D->base==D->top) return ERROR;
*number=*--D->top;
return OK;
}
//对栈顶符号和待入栈符号进行优先级比较
char Precede(char a, RElement b) { //判断符号优先级
char op;
switch(a){
case '#':
if (b=='=')
op='=';
else op='<';
break;
case '+':
case '-':
if (b=='*' || b=='/' || b=='(' || b=='^')
op='<';
else op='>';
break;
case '*':
case '/':
if (b=='(' || b=='^')
op='<';
else op='>';
break;
case '^':
if(b=='(')
op='<';
else op='>' ;
break;
case '(':
if (b==')')
op='=';
else op='<';
break;
case ')':
op='>';
break;
}
return op;
}
//对表达式的部分进行运算
double Operate( double d, char e ,double f){ //数值计算
double g;
switch(e){
case '+':
g=d+f;
break;
case '-':
g=d-f;
break;
case '*':
g=d*f;
break ;
case '/':
g=d/f;
break;
case '^':
g=pow(d,f);
break;
}
return g;
}
3.2 主函数
int main(){
char str[200],str_copy[200];
int i,j,str_flag;
double a,b,result;
char flag,char_e,op;
SqStackR RStack;
SqStackD DStack;
InitSqStackList_R(&RStack);
InitSqStackList_D(&DStack);
printf("*********************************************************\n");
printf("*名称:基于栈的中缀算数表达式求值 *\n");
printf("*说明: 1.该程序可计算浮点数 *\n");
printf("* 2.参与运算的数为正数 *\n");
printf("* 3.当输入只有“=”时程序结束运行 *\n");
printf("*********************************************************\n");
while(OK){
printf("请输入算术表达式:");
scanf("%s",str);
str_flag=strcmp(str,"=");
if(!str_flag) break;
strcpy(str_copy,str);
Push_R(&RStack,'#');
for(i=0;str[i]!='\0';i++){
if(str[i]>='0'&&str[i]<='9'){
a=atof(str); //提取当前字符串中第一个数字
Push_D(&DStack,a); //将提取到的数字压栈
for(j=i; ;j++){
if(str[j]=='+'||str[j]=='-'||str[j]=='*'||str[j]=='/'||str[j]=='('||str[j]==')'||str[j]=='=') break;
} //判断最后提取的数字的最后一位的位置
for(i;i<j;i++) str[i]=' '; //将提取出数字的位置赋值为空,以便下一次提取数字
i--;
}
else{
flag=Precede((char)GetTop_R(RStack),(RElement)str[i]);
//提取到运算符,并与运算符栈栈顶进行优先级比较
if(flag=='<'){ //小于,压栈
Push_R(&RStack,str[i]); str[i]=' ';
}
if(flag=='='){ //等于,出栈抵消
Pop_R(&RStack,&char_e);str[i]=' ';
}
if(flag=='>'){ //大于,出栈栈顶运算符和栈顶运算数并进行一次计算
Pop_R(&RStack,&op);
Pop_D(&DStack,&b);
Pop_D(&DStack,&a);
result=Operate(a,op,b);
Push_D(&DStack,result);
i--;
}
}
}
if(RStack.base==RStack.top){
//当运算符栈清空,同时字符串结束,计算结束,输出结果
Pop_D(&DStack,&result);
printf("运算结果为:%s%0.2f\n",str_copy,result);
}
else{ //输入字符串错误
printf("输入表达式不合法,请重新输入!\n");
}
}
printf("运行结束,欢迎下次使用!\n");
return 0;
}
4 调试分析
4.1 问题解决及设计实现的回顾讨论与分析
(1)程序GetTop函数运行返回值错误
问题:当主函数运行到flag=Precede((char)GetTop_R(RStack),(RElementstr[i]);时返回的字符总为“?”,而不是应当返回的“#”或其他字符。
解决:通过在主函数中增加printf函数,将各步骤的执行情况输出到屏幕,最终判断出是Push函数出现错误,没有将数据成功入栈。而Push错误的原因在于if的“{}”的错误位置:“*R->top++=sign;”错误的将该语句放入了if内部。
(2)主程序进入死循环,无法退出
问题:因为atof只能跳过字符串前面的空格而对字符串中double型数字进行转换,因此当读入一个double数字或运算符后都需对其所在位置置空格。而因置空格的条件出错,因而导致主程序进入死循环。
解决:更换置空格条件。原先的条件为“if(!((str[i]>=’0’&&str[i]>=’9’)||str[i]==’.’)) break;”,更改后为“ if(str[j]=='+'||str[j]=='-'||str[j]=='*'||str[j]=='/'||str[j]=='('||str[j]==')'||str[j]=='=') break;”,置空格模块如下图
4.2 算法时空复杂度分析
4.3 经验与体会
(1)当某一个函数调用时发生错误,问题不一定出在调用的这个函数上,也需要同时考虑与它相关联的其它函数,甚至于检查整个程序最底层、最基础的函数。所谓“基础不牢,地动山摇”就是这个意思。在此次的程序编写过程中就因为最基础的入栈函数编写错误,才导致了后面在调用GetTop和Precede时,两个函数的返回值都为“?”,造成程序错误。
(2)当主函数进入死循环,大概率都是因为循环不当造成,而此时最好的解决方法就是将控制循环的变量输出在屏幕上进行分析。在得到分析结果后便可以重新设计循环条件等,以此来纠正循环错误。
(3)不要过分贪图代码精简。在此次编写程序的过程中就是置空格模块的代码过于追求精简才导致了死循环的问题。在某些情况下if的判断条件适当采用简单的代码进行描述,可以减少错误的发生。
5 测试数据与结果
依次输入: 3*9=
(3.5-0.5)*2.3=
(5.1-1.1)/2=
(13.14-5.2)*(5.21-5.2)=
=
输出结果: 27.00
6.90
2.00
0.08
运行结束,欢迎下次使用!