标签:
11、蛤蟆的数据结构笔记之十一栈的应用之表达式求值实现
本篇名言:“人生不售来回票,一旦动身,绝不能复返。”
继续栈应用实现,这次是来看下表达式求值的栈实现。
欢迎转载,转载请标明出处:
表达式求值是设计语言编译中的一个基本问题,它的实现是栈应用的又一个典型例子。
任何一个表达式都是由操作数(Operand)、运算符(operator)和界限符(delimiter)组成。
操作数可以是常数也可以是变量或变量的标识符。
运算符可以分为算术运算符、关系运算符和逻辑运算符三类。
界限符有左右括号和表达式结束符。
几个算术四则运算的规则:
n 先乘除,后加减;
n 从左算到右
n 先括号内,后括号外
此处实现,假定不出现语法错误,否则过于复杂,蛤蟆也不能理解了。
对于连个相继出现的操作符θ1和θ2 有三种关系:大于、等于和小于。由此可以列出“+-*/”之间的优先级。如下表:
|
+ |
- |
* |
/ |
( |
) |
# |
+ |
> |
> |
< |
< |
< |
> |
> |
- |
> |
> |
< |
< |
< |
> |
> |
* |
> |
> |
> |
> |
< |
> |
> |
/ |
> |
> |
> |
> |
< |
> |
> |
( |
< |
< |
< |
< |
< |
= |
|
) |
> |
> |
> |
> |
|
> |
> |
# |
< |
< |
< |
< |
< |
|
= |
加减乘除优先性都低于“(”但是高于“)”,由运算从左到右可知,当θ1=θ2 ,令θ1>θ2
为了算法简洁,在表达式的左边和右边虚设一个“#”,这一对“#”表示一个表达式求值完成。
“(”=“)”当一对括号相遇时表示括号内已运算完成。
“)”和“(”、“#”和“(”、“(”和“#”无法相继出现如果出现则表达式出现语法错误。
为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存运算数和运算结果。
l 首先置操作数栈为空栈,表达式起始符为“#”为栈底元素。
l 依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先权作相应操作,直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为“#”)。
根据算法基本思路,代码实现如下:
int EvaluateExpression(){
intflag;
charc;
charx,theta;
chara,b;
charOP[]="+-*/()#";
SqStack OPTR;
SqStack OPND;
InitStack(&OPTR);
Push(&OPTR,‘#‘);
InitStack(&OPND);
flag=getNext(&c);
GetTop(OPTR,&x);
while(c!=‘#‘|| x != ‘#‘)
{
if(flag== 0)
{
Push(&OPND,c);
flag= getNext(&c);
}
else
{
GetTop(OPTR,&x);
switch(Precede(x,c))
{
case ‘<‘://栈顶元素优先级低
Push(&OPTR,c);
flag= getNext(&c);
break;
case ‘=‘://脱括号并接受下一字符
Pop(&OPTR,&x);
flag= getNext(&c);
break;
case ‘>‘://退栈并将运算结果入栈
Pop(&OPTR,&theta);
Pop(&OPND,&b);
Pop(&OPND,&a);
Push(&OPND,Operate(a, theta, b));
break;
}
}
GetTop(OPTR,&x);
}
GetTop(OPND,&c);
freestack(&OPTR);
freestack(&OPND);
returnc;
}
Main函数实现
void main(){
intc;
printf("Pleaseinput one expression:");
c=EvaluateExpression();
printf("Result=%d\n",c);
}
结果如下图1所示:
可以直接在VS上进行编译运行。
#include "stdio.h"
#include "stdlib.h"
#include "ctype.h"
typedef int Status;
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char SElemType; /*放入堆栈的元素的类型*/
typedef struct{
SElemType*base;
SElemType*top;
int stacksize;
}SqStack;
//构造一个空栈
Status InitStack(SqStack *S){
S->base= (SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!S->base)
exit(-1);
S->top=S->base;
S->stacksize=STACK_INIT_SIZE;
return1;
}
void freestack(SqStack *S){
free(S->base);
}
//判断是否为空栈
Status StackEmpty(SqStack S){
if(S.top== S.base)
return1;
else
return0;
}
//用e返回S的顶元素
Status GetTop(SqStack S, SElemType *e){
if(S.top== S.base)
return-1;
*e =*(S.top-1);
return1;
}
//插入e为新的顶元素
Status Push(SqStack *S, SElemType e){
if((S->top- S->base) >= S->stacksize){
S->base= (
SElemType*)realloc(S->base,
(S->stacksize+STACKINCREMENT)*sizeof(SElemType)
);
if(!S->base)
exit(-1);
S->top= S->base +S->stacksize;
S->stacksize+= STACKINCREMENT;
}
*(S->top)=e;
S->top++;
return1;
}
//删除S的顶元素,并用e返回其值
Status Pop(SqStack *S, SElemType *e){
if(S->top== S->base)
return-1;
S->top--;
*e =*(S->top);
return1;
}
//从栈底到栈顶依次对S的每个元素调用函数Visit(),一旦失败操作无效
Status ListTraverse(SqStack S,Status(*visit)(SElemType)){
SElemType*p;
p=S.base;
for(p=S.base;p<S.top;p++)
(*visit)(*p);
return1;
}
//输出元素e
Status output(SElemType e){
printf("%d",e);
return1;
}
/*计算整数表达式的值
*表达式必须以#结束
*表达式中可以出现多位数字,
*表达式中可以出现空格
*运算符包括+,-,*,/,(,)
*运算结果可以是多位整数,并以整数的形式返回
*/
/*判断输入的某个字符是否是运算符
*c表示输入的字符
*op数组中存放系统能识别的运算符
*/
Status in(char c,char op[]){
char*p;
p=op;
while(*p!= ‘\0‘){
if(c== *p)
return1;
p++;
}
return0;
}
/*比较两个运算符的优先级
*a,b中存放待比较的运算符
*‘>‘表示a>b
*‘0‘表示不可能出现的比较
*/
char Precede(char a, char b){
inti,j;
charpre[][7]={
/*运算符之间的优先级制作成一张表格*/
{‘>‘,‘>‘,‘<‘,‘<‘,‘<‘,‘>‘,‘>‘},
{‘>‘,‘>‘,‘<‘,‘<‘,‘<‘,‘>‘,‘>‘},
{‘>‘,‘>‘,‘>‘,‘>‘,‘<‘,‘>‘,‘>‘},
{‘>‘,‘>‘,‘>‘,‘>‘,‘<‘,‘>‘,‘>‘},
{‘<‘,‘<‘,‘<‘,‘<‘,‘<‘,‘=‘,‘0‘},
{‘>‘,‘>‘,‘>‘,‘>‘,‘0‘,‘>‘,‘>‘},
{‘<‘,‘<‘,‘<‘,‘<‘,‘<‘,‘0‘,‘=‘}};
switch(a){
case ‘+‘:i=0; break;
case ‘-‘:i=1; break;
case ‘*‘:i=2; break;
case ‘/‘:i=3; break;
case ‘(‘:i=4; break;
case ‘)‘:i=5; break;
case ‘#‘:i=6; break;
}
switch(b){
case ‘+‘:j=0; break;
case ‘-‘:j=1; break;
case ‘*‘:j=2; break;
case ‘/‘:j=3; break;
case ‘(‘:j=4; break;
case ‘)‘:j=5; break;
case ‘#‘:j=6; break;
}
returnpre[i][j];
}
/*进行实际的运算
*a,b中分别以整数的形式存放两个待运算的操作数
*theta中存放代表操作符的字符
*结果以整数的形式返回
*/
int Operate(int a, char theta, int b){
inti,j,result;
i=a;
j=b;
switch(theta) {
case ‘+‘:result = i + j; break;
case ‘-‘:result = i - j; break;
case ‘*‘:result = i * j; break;
case ‘/‘:result = i / j; break;
}
returnresult;
}
/*从输入缓冲区中获得下一个整数或运算符,并通过n带回到主调函数
*返回值为1表示获得的是运算符
*返回值为0表示获得的是整形操作数
*/
int getNext(char *n){
charc;
*n=0;
while((c=getchar())==‘‘); /*跳过一个或多个空格*/
if(!isdigit(c)){ /*通过函数判断如果字符不是数字,那么只能是运算符*/
*n=c;
return1;
}
do{ /*能执行到该条语句,说明字符是数字,此处用循环获得连续的数字*/
*n=*n*10+(c-‘0‘); /*把连续的数字字符转换成相对应的整数*/
c=getchar();
} while(isdigit(c)); /*如果下一个字符是数字,进入下一轮循环*/
ungetc(c,stdin); /*新读入的字符不是数字,可能是运算符,为了不影响下次读入,把该字符放回到输入缓冲区*/
return0;
}
int EvaluateExpression(){
intflag;
charc;
charx,theta;
chara,b;
charOP[]="+-*/()#";
SqStack OPTR;
SqStack OPND;
InitStack(&OPTR);
Push(&OPTR,‘#‘);
InitStack(&OPND);
flag=getNext(&c);
GetTop(OPTR,&x);
while(c!=‘#‘|| x != ‘#‘)
{
if(flag== 0)
{
Push(&OPND,c);
flag= getNext(&c);
}
else
{
GetTop(OPTR,&x);
switch(Precede(x,c))
{
case ‘<‘://栈顶元素优先级低
Push(&OPTR,c);
flag= getNext(&c);
break;
case ‘=‘://脱括号并接受下一字符
Pop(&OPTR,&x);
flag= getNext(&c);
break;
case ‘>‘://退栈并将运算结果入栈
Pop(&OPTR,&theta);
Pop(&OPND,&b);
Pop(&OPND,&a);
Push(&OPND,Operate(a, theta, b));
break;
}
}
GetTop(OPTR,&x);
}
GetTop(OPND,&c);
freestack(&OPTR);
freestack(&OPND);
returnc;
}
void main(){
intc;
printf("Pleaseinput one expression:");
c=EvaluateExpression();
printf("Result=%d\n",c);
}
标签:
原文地址:http://blog.csdn.net/notbaron/article/details/46540073