表达式求值.docx
《表达式求值.docx》由会员分享,可在线阅读,更多相关《表达式求值.docx(12页珍藏版)》请在冰豆网上搜索。
表达式求值
数据结构课程设计报告
项目名称:
表达式求值
学号:
班级:
姓名:
指导老师:
计算机科学与技术
2014年12月20日
1.问题描述:
表达式求值问题主要是通过使用栈和队列来解决问题。
我们在书写表达式时总是以中缀形式输入,也就是将运算符放在操作数中间,但是计算机处理数据时,运算符却跟在两个操作数之后,这种表现形式称为后缀表达式,所以在书写程序时要注意两种表现形式之间的转换。
2.设计思路:
①一个表达式由运算符和操作数两部分构成,它的操作数由0-9这十个数字构成,它的运算符有+、-、*、/四种。
一般在表达式中,括号也会改变表达式运算的优先级,所以将括号也看做运算符,并将其的优先级规定为最低。
②通过栈将中缀表达式转换成后缀表达式的算法思想:
顺序扫描中缀表达式,当读到数字时直接将其送至队列中,当读到运算符时,将栈中所有优先级高于或等于该运算符的运算符出栈,送至输出队列中,再将当前运算符入栈,当读到左括号时,入栈,当读到右括号时,将靠近栈顶的第一个左括号上面的运算符全部依次出栈,送至输出队列中,再删除栈中的左括号。
为了方便边界条件(栈空)的判断,提高算法的运行效率,在扫描读入中缀表达式之前、在空栈中预先压入一个“#”字符作为栈底元素,另外,在表达式的最后增加一个“#”字符作为中缀表达式的结束标志,该结束符与栈底元素“#”配对。
本算法不包括输入表达式的语法检查,但可以过滤掉输入符号之间的空格。
执行上述程序若输入中缀表达式字符串:
9-(2*4=7)/5+3#,就会得到后缀表达式:
9247*+5/3+.其中,运算符栈和存放后缀表达式的队列的变化过程如表2—1所示。
表2—1中缀表达式到后缀表达式的转换过程示例
转换步骤
中缀表达式的读入
运算符栈OS
后缀表达式PostQ
初始
9-(2+4*7)/5+3#
#
空
1
-(2+4*7/5+3#
#
9
2
(2+4*7)/5+3#
-
9
3
2+4*7)/5+3#
-(
9
4
+4*7)/5+3#
-(
92
5
4*7)/5+3#
-(+
92
6
*7)/5+3#
-(+
924
7
7)/5+3#
-(+*
924
8
)/5+3#
-(+*
9247
9
/5+3#
-
9247*+
10
5+3#
-/
9247*+
11
+3#
-/
9247*+5
12
3#
+
9247*+5/-
13
#
+
9247*+5/-3
14
空
9247*+5/-3+
③.后缀表达式的计算
在后缀表达式中,不仅不需要括号,而且还完全免除了运算符优先级规则。
对于后缀表达式来说,仅仅使用一个自然规则,即从左到右顺序完成计算,这个规则对于计算机而言是很容易实现的。
下面将讨论如何用就算计算机了实现计算后缀表达式的算法。
如果在表达式中只有一个运算符,如像53*这样的表达式,显然计算过程非常重要,可立即执行。
但在多数情况下,后缀表达式中都是多于一个运算符,因此,必须要像保存输入数字一样保存其中间结果。
在算法中要有一个数字字符到数值的转换。
下面以后缀表达式9247*+5/-3+为例,使用上述算法计算该表达式,计算过程如表2-2所示
表2-2后缀表达式的计算过程示例
计算步骤
后缀表达式的读入
运算结果栈VS
初始
9247*+5/-3+
空
1
247*+5/-3+
9
2
47*+5/-3+
29
3
7*+5/-3+
429
4
*+5/-3+
7429
5
+5/-3+
2829
6
5/-3+
3029
7
/-3+
5309
8
-3+
69
90
3+
3
10
+
33
11
空
6
3.数据结构定义:
//操作数栈定义
typedefstruct
{
floatdata[MaxSize];
inttop;
}OpStack,SeqStack;
4.系统功能模块介绍:
()
流程图如下图所示:
5.运行与调试分析:
输入表达式:
9/(8-5)+6
6.体会:
这次课程设计让我更加了解大一学到的C和数据结构程序设计与算法。
设计题目要求不仅要求对课本知识有较深刻的了解,同时要求程序设计者有较强的思维和动手能力和更加了解编程思想和编程技巧。
这次课程设计让我有一个深刻的体会,那就是细节决定成败,编程最需要的是严谨,如何的严谨都不过分,往往检查了半天发现错误发生在某个括号,分号,引号,或者数据类型上。
就像我在写PushStack()函数时,忘了指针的地址符值不用加*号,这一点小小的错误也耽误了我几十分钟,所以说细节很重要。
设计程序时,也要不怕遇到错误,否则会让自己心情烦躁,很难找出错误,要冷静思考。
同时也要善于请教别人,因为人总是很难发现自己的错误。
在这次程序设计中,我在了解和掌握数据结构程序设计的过程中,发现了自己许多的不足之处,在以后的上机中应更加注意,同时体会到C语言具有的语句简洁,使用灵活,执行效率高等特点,也发现上机的重要作用,特别对算术表达式有了深刻的理解。
主程序:
#include
#include
typedefcharDataType;
#defineMaxSize50
//操作数栈定义
typedefstruct
{
floatdata[MaxSize];
inttop;
}OpStack,SeqStack;
//函数声明
voidInitStack(SeqStack*s);//初始化
intStackEmpty(SeqStacks);//判断栈是否为空
intGetTop(SeqStacks,DataType*e);//取栈顶元素
intPushStack(SeqStack*s,DataTypee);//入栈
intPopStack(SeqStack*s,DataType*e);//出栈
voidTranslateE(chars1[],chars2[]);//将中缀表达式转化为后缀表达式
floatComputeE(chars[]);//计算后缀表达式
voidmain()
{
chara[MaxSize],b[MaxSize];
floatf;
printf("请输入表达式:
\n");
gets(a);
printf("中缀表达式为:
%s\n",a);
TranslateE(a,b);
printf("转换为后缀表达式为:
%s\n",b);
f=ComputeE(b);
printf("该表达式的计算结果为:
%f\n",f);
}
floatComputeE(chara[])//计算后缀表达式的值
{
OpStacks;//定义一个操作数栈
inti=0;
floatx1,x2,value,result;
s.top=-1;//初始化栈
while(a[i]!
='\0')//依次扫描后缀表达式中的每个字符
{
if(a[i]!
=''&&a[i]>='0'&&a[i]<='9')//如果当前字符是数字字符,转换为数字
{
value=0;
while(a[i]!
='')//如果不是空格,说明数字字符是两位数以上的
{
value=10*value+a[i]-'0';
i++;
}
s.top++;
s.data[s.top]=value;//处理之后将数字进栈
}
else//如果当前字符是运算符
{
switch(a[i])//将栈中的数字出栈两次,然后用当前的运算符进行运算,再将结果入栈
{
case'+':
{
x1=s.data[s.top];
s.top--;
x2=s.data[s.top];
s.top--;
result=x1+x2;
s.top++;
s.data[s.top]=result;
break;}
case'-':
{
x1=s.data[s.top];
s.top--;
x2=s.data[s.top];
s.top--;
result=x2-x1;
s.top++;
s.data[s.top]=result;
break;}
case'*':
{
x1=s.data[s.top];
s.top--;
x2=s.data[s.top];
s.top--;
result=x1*x2;
s.top++;
s.data[s.top]=result;
break;}
case'/':
{
x1=s.data[s.top];
s.top--;
x2=s.data[s.top];
s.top--;
result=x2/x1;
s.top++;
s.data[s.top]=result;
break;}
}
i++;
}
}
if(s.top!
=-1)//如果栈不空,将结果出栈,并返回
{
result=s.data[s.top];
s.top--;
if(s.top==-1)
returnresult;
else
{
printf("表达式错误");
return(-1);
}
}
elsereturn-1;
}
voidTranslateE(charstr[],charexp[])
{
SeqStacks;
charch;
DataTypee;
inti=0,j=0;
InitStack(&s);
ch=str[i];
i++;
while(ch!
='\0')//依此扫描中缀表达式中的每个字符
{
switch(ch)
{
case'(':
//遇到左括号,将其进栈
PushStack(&s,ch);
break;
case')':
//遇到右括号,将栈中的操作数出栈,并将其存入数组exp中
while(GetTop(s,&e)&&e!
='(')
{
PopStack(&s,&e);
exp[j]=e;
j++;
}
PopStack(&s,&e);//将左括号出栈
break;
case'+':
case'-':
//遇到加减号,将栈顶字符出栈,并将其存入数组exp中,然后将当前运算符进栈
while(!
StackEmpty(s)&&GetTop(s,&e)&&e!
='(')
{
PopStack(&s,&e);
exp[j]=e;
j++;
}
PushStack(&s,ch);
break;
case'*':
//遇到乘除号,先将同级运算符出栈,并存入数组exp中,然后将当前的运算符进栈
case'/':
while(!
StackEmpty(s)&&GetTop(s,&e)&&e=='/'||e=='*')
{
PopStack(&s,&e);
exp[j]=e;
j++;
}
PushStack(&s,ch);
break;
case'':
break;
default:
//遇到操作数,则将操作数直接送入数组exp中,并在其后添加一个空格,用来分隔数字字符
while(ch>='0'&&ch<='9')
{
exp[j]=ch;
j++;
ch=str[i];
i++;
}
i--;
exp[j]='';
j++;
}
ch=str[i];//读入下一个字符
i++;
}
while(!
StackEmpty(s))//将栈中所有剩余的运算符出栈,送入数组exp中
{
PopStack(&s,&e);
exp[j]=e;
j++;
}
exp[j]='\0';
}
voidInitStack(SeqStack*s)//初始化
{
s->top=0;//把栈顶指针置为0
}
intStackEmpty(SeqStacks)//判断栈是否为空
{
if(s.top==0)//判断栈顶指针是否为0
return1;
else
return0;
}
intGetTop(SeqStacks,DataType*e)//取栈顶元素
{
if(s.top<=0)//在取栈顶元素之前,判断栈是否为空
{
printf("栈已为空!
\n");
return0;
}
else
{
*e=(char)s.data[s.top-1];//取栈顶元素
return1;
}
}
intPushStack(SeqStack*s,DataTypee)//入栈
{
if(s->top>=MaxSize)//判断栈是否满了
{
printf("栈已满,不能进栈!
\n");
return0;
}
else
{
s->data[s->top]=e;//e元素进栈
s->top++;//修改栈顶指针
return1;
}
}
intPopStack(SeqStack*s,DataType*e)//出栈
{
if(s->top==0)
{
printf("栈已空,不能出栈!
\n");
return0;
}
else
{
s->top--;
*e=(char)s->data[s->top];
return1;
}
}