表达式求值(数据结构栈,c语言版)
一、实验题目
1.案例分析
任何一个表达式都是由操作数(operand)运算符(operator)和界限符(delimiter)组成的,统称它们为单词。一般地,操作数既可以是常数,也可以是被说明为变量或常量的标识符;运算符可以分为算术运算符、关系运算符和逻辑运算符 3 类;基本界限符 有左右括号和表达式结束符等。为了叙述的简洁,在此仅讨论简单算术表达式的求值问题,这种表达式只含加、减、乘、除4种运算符。读者不难将它推广到更一般的表达式上。 下面把运算符和界限符统称为算符。 我们知道,算术四则运算遵循以下 3条规则: (1)先乘除,后加减; (2)从左算到右; (3)先括号内,后括号外。 根据上述 3条运算规则,在运算的每一步中,任意两个相继出现的算符θ1和θ2之间的优先关系,至多是下面 3 种关系之一 : θ1 < θ2 θ1的优先权低于θ2 θ1 = θ2 θ1的优先权等于θ2 θ1 > θ2 θ1的优先权高于θ2 表 3.1 定义了算符之间的这种优先关系。 
由规则(1), 先进行乘除运算,后进行加减运算,所以有 “+” < “*”; “+” < “/”; “*” >"+"; “/” > “+” 等。 由规则(2), 运算遵循左结合性,当两个运算符相同时,先出现的运算符优先级高,所以有"+" > “+”; “-” > “-”; “*” > “*”; “/” > “/”。 由规则(3), 括号内的优先级高,+、-、*和/为θ1时的优先性均低千 (" “但高于 " )”。 表中的 “(” = “)” 表示当左右括号相遇时,括号内的运算已经完成。为了便千实现,假设每个表达式均以"#“开始,以”#" 结束。所以"#" = “#” 表示整个表达式求值完毕。")“与 “(”、"#”与”)" 以及"(“与”#" 之间无优先关系,这是因为表达式中不允许它们相继出现,一旦遇到这种情况,则可以认为出现了语法错误。在 下面的讨论中,我们暂假定所输人的表达式不会出现语法错误。
2.案例实现
为实现算符优先算法,可以使用两个工作栈,一个称做OPTR,用以寄存运算符;另一个称作OPND, 用以寄存操作数或运算结果。
3.算法步骤
1.初始化OPTR栈和OPND栈,将表达式起始符“#”压入OPTR栈。 2.扫描表达式,读人第一个字符ch,如果表达式没有扫描完毕至“#”或OPTR的栈顶元素不为“#”时,则循环执行以下操作: 若ch不是运算符,则压入OPND栈,读入下一字符ch; 若ch是运算符,则根据OPTR 的栈顶元素和ch的优先级比较结果,做不同的处理: 若是小于,则ch 压入OPTR栈,读入下一字符ch; 若是大于,则弹出OPTR栈顶的运算符,从 OPND栈弹出两个数,进行相应运算,结果压入OPND栈; 若是等于,则OPTR 的栈顶元素是“(”且ch是“)”,这时弹出OPTR栈顶的“(”,相当于括号匹配成功,然后读人下一字符ch。 3.OPND栈顶元素即为表达式求值结果,返回此元素。
char EvaluateExpression () {//算术表达式求值的算符优先算法,设OPTR和OPND分别为运算符栈和操作数栈 InitStack(OPND); //初始化OPND栈 InitStack(OPTR); //初始化OPTR栈 Push (OPTR,’#’) ; // 将表达式起始符"#" 压人OPTR栈 cin>>ch; while(ch!=’#’ | | GetTop(OPTR) !=’#’ ){ //表达式没有扫描完毕或OPTR的栈顶元素不为"# " if (!In (ch)) {Push (OPND, ch); cin?ch;} //ch不是运算符则进OPND栈 else switch (Precede (GetTop (OPTR) , ch)){ / /比较OPTR的栈顶元素和ch的 优先级 case’<’: Push(OPTR,ch);cin>>ch; //当前字符ch压入OPTR栈,读入下一字符ch break; case’>’: Pop(OPTR,theta); //弹出OPTR栈顶的运算符 Pop(OPND,b);Pop(OPND,a); //弹出OPND栈顶的两个运算数 Push (OPND, Operate (a, theta, b·)); / /将运算结果压入OPND栈 break; case’=’: //OPTR的栈顶元素是"(“且ch是”)" Pop(OPTR,x) ;cin>>ch; //弹出OPTR栈顶的"(", 读入下一字符ch break; }//switch }//while return GetTop (OPND) ; //OPND栈顶元素即为表达式求值结果 }
算法调用的三个函数需要读者自行补充完成。其中函数In是判定读入的字符ch是否为运算符,Precede 是判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数,Operate为进行二元运算的函数。
二、工具环境
Window10操作系统,Microsoft Visual C++2010学习版 集成开发环境,C语言
三、实验问题
另外需要特别说明的是,上述算法中的操作数只能是一位数,因为这里使用的OPND栈是字符栈,如果要进行多位数的运算,则需要将OPND栈改为数栈,读入的数字字符拼成数之后再入栈。 读者可以改进此算法,使之能完成多位数的运算。
四、实验代码
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
typedef char SElemType;
typedef struct
{
char *base;
char *top;
int stacksize;
}SqStack;
Status InitStack(SqStack *S);
Status Push(SqStack *S,char e);
Status Pop(SqStack *S,char *e);
SElemType GetTop(SqStack S);
Status In(char e);
SElemType Precede(char a,char b);
int Operate(int i,char theta,int j);
char EvaluateExpression();
int main()
{
printf("请输入算术表达式,并以#结束(操作数只能是一位数):");
printf("表达式结果是:%d",EvaluateExpression());
return 0;
}
Status InitStack(SqStack *S)
{
S->base=(char *)malloc(MAXSIZE*sizeof(char));
if(!S->base) exit(OVERFLOW);
S->top=S->base;
S->stacksize=MAXSIZE;
return OK;
}
Status Push(SqStack *S,char e)
{
if(S->top-S->base==S->stacksize) return ERROR;
*S->top++=e;
return OK;
}
Status Pop(SqStack *S,char *e)
{
if(S->top==S->base) return ERROR;
*e=*--S->top;
return OK;
}
SElemType GetTop(SqStack S)
{
if(S.top!=S.base)
return *(S.top-1);
}
Status In(char e)
{
if(e=='+'||e=='-'||e=='*'||e=='/'||e=='('||e==')'||e=='#')
return OK;
else
return ERROR;
}
SElemType Precede(char a,char b)
{
char f;
if(a=='+'||a=='-')
{
if(b=='+'||b=='-'||b==')'||b=='#')
f='>';
else if(b=='*'||b=='/'||b=='(')
f='<';
}
else if(a=='*'||a=='/')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b==')'||b=='#')
f='>';
else if(b=='(')
f='<';
}
else if(a=='(')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b=='(')
f='<';
else if(b==')')
f='=';
}
else if(a==')')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b==')'||b=='#')
f='>';
}
else if(a=='#')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b=='(')
f='<';
else if(b=='#')
f='=';
}
return f;
}
int Operate(int i,char theta,int j)
{
int result;
switch(theta) {
case '+': result = i + j; break;
case '-': result = i - j; break;
case '*': result = i * j; break;
case '/': result = i / j; break;
}
return result;
}
char EvaluateExpression()
{
SqStack OPND,OPTR;
int ch;
char a,b,theta,x;
InitStack(&OPND);
InitStack(&OPTR);
Push(&OPTR,'#');
ch=getchar();
while(ch!='#'||GetTop(OPTR)!='#')
{
printf(" %c\n",ch);
if(!In(ch))
{
ch=ch-48;
Push(&OPND,ch);
ch=getchar();
}
else
{
switch(Precede(GetTop(OPTR),ch))
{
case '<':
Push(&OPTR,ch);
ch=getchar();
break;
case '>':
Pop(&OPTR,&theta);
Pop(&OPND,&b);
Pop(&OPND,&a);
Push(&OPND,Operate(a,theta,b));
break;
case '=':
Pop(&OPTR,&x);
ch=getchar();
break;
}
}
}
return GetTop(OPND);
}
|