导读
本篇文章将会介绍栈的一个相对复杂的应用——表达式求值,将会包含完整的C语言代码以及测试图,代码完全手打,若存在不足还请指出。
表达式求值
表达式求值是程序设计语言编译中的一个最基本的问题,它的实现是栈应用的又一个典型例子,这里介绍一种简单直观、广为使用的算法,通常称为“算符优先法”。
对于一个表达式的求解,我们要依照四则运算的法则去将其翻译成计算机能够执行的一个个步骤。
比如4+2*3-10/5
其运算顺序应该为4+6-10/5——4+6-2——10-2——8
算符优先法就是根据这个运算优先关系的规定来实现对表达式的编译或者解释执行的。
一个表达式中有运算符、操作数、界限符
运算符假设只有+ - * /
操作数就是表达式中的数字或被说明为变量或常量的标识符
界限符包括了括号“()”或者结束符,为了方便期间我们把“#”作为起始符与结束符
下面讨论优先级的问题,假设在表达式相继遇到了两个运算符θ1和θ2,若θ1<θ2表示后面的运算符的优先级大于前面,>同理,若θ1=θ2,只有两种可能,一种是“(” 遇到 “)”(表示括号内运算已经完成),一种是#遇到#(表示整个表达式已经算完)。
除此之外,还有三种格式错误的情况,用×表示,如下:
下面简述一下整个步骤
首先我们需要准备几个栈的函数:初始化栈、取栈顶、入栈、出栈(这四个函数需要两套,分别用于字符栈和数字栈),其中字符栈(OPTR)用来存放运算符,数字栈(OPND)用来存放操作数。
然后需要依次定义三个特殊的函数
1.isOPTR(c):判断这个是不是运算符
2.Precede(c1,c2):判断两个运算符的优先级
3.Operate(a,sign,b):进行a与b的sign(加减乘除)运算
刚开始,我们初始化数栈与字符栈,然后让起始符“#”入字符栈,然后读取第一个字符c,进入循环,这个循环的条件是什么呢?
进入循环,表示运算没有结束,两种可能:
1.读取的字符不是结束符“#”
2.此时字符栈栈顶元素不是起始符“#”
进入循环之后去判断c是不是运算符,如果不是,说明c为操作数,则将其入数字栈,并且往下读入一个新字符c
如果c是运算符,需要利用Precede函数去比较字符栈栈顶的运算符与c的优先级大小,如果栈顶运算符优先级较小,则让c入字符栈并且往下再读一个字符c
如果栈顶运算符优先级与c相同,只有一种可能性就是括号里面的运算已经完成,那么让栈顶的左括号出栈,并且往下再读一个字符
如果栈顶运算符优先级大于c,那么先让字符栈栈顶元素出栈并保存为sign,分别用a和b保存数字栈先后出栈的两个数字,然后利用Operate函数去进行a和b的运算。
一直循环,直到结束,返回数字栈栈顶元素,即为最终结果。
看完步骤,我们会很明显地看到这个方法的局限性,就是每次读入的只是char类型的一个字符,进入数字栈里面的所有的操作数只能是一位数,有什么办法,可以实现多位数的求值呢?
在这里我想到了其中一种方法,仅供参考:
设置一个标记flag,初始化为0,如果读入了一个数字(即!isOPTR(c)),进入循环开始的if里面,就让flag=1,并且入栈,再接受一个新字符,如果这个新字符是运算符,那么在下一个循环当中,让flag重新变为0,但如果接受的新字符,还是数字,由于此时flag=1,让其在下一个循环当中,进入一个新的函数叫做NewNumber(a,b),其中a是数字栈栈顶元素(出栈后得到),b是一位数的新字符,在这个函数里面,让a与b进行“拼接”,返回拼接后的数字重新入数字栈即可。
比如:
刚开始我输入“2”,再输入“4”,进入NewNumber里面,返回“24”
但是需要注意的是整型与字符型的数字之间差了一个字符0 (‘0’)
下面给出完整代码,与测试图:
//表达式求值
//输入格式:先输入表达式最后再输入一个# 然后按回车就出现结果
//支持括号
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define ERROR 0
#define OK 2
#define OVERFLOW 0
typedef int Status;
typedef struct NumStackNode{
int data;
struct NumStackNode* next;
}NumStackNode,*NumStackNodePtr;
typedef struct{
NumStackNodePtr top;
NumStackNodePtr bottom;
}NumStack,*NumStackPtr; //数字栈
typedef struct CharStackNode{
char data;
struct CharStackNode *next;
}CharStackNode,*CharStackNodePtr;
typedef struct{
CharStackNodePtr top;
CharStackNodePtr bottom;
}CharStack,*CharStackPtr; //字符栈
NumStackPtr NumInitStack(void);//初始化数字栈
CharStackPtr CharInitStack(void);//初始化字符栈
Status NumGetTop(NumStackPtr S,int* e);//取数字栈栈顶
Status CharGetTop(CharStackPtr S,char* e);//取字符栈栈顶
Status NumPush(NumStackPtr S,int e);//数字栈入栈
Status CharPush(CharStackPtr S,char e);//字符栈入栈
Status NumPop(NumStackPtr S,int *e);//数字栈出栈
Status CharPop(CharStackPtr S,char *e);//字符栈出栈
Status isOPTR(char c);//判断是不是字符
char Precede(char c1,char c2);//比较优先级
int Operate(int a,char sign,int b);//运算
int NewNumber(int a,int b);//链接更新数字
void EvaluateExpression(int *n);//表达式求值
int main()
{
printf("输入表达式:");
int n;
EvaluateExpression(&n);
printf("值为:%d\n",n);
return 0;
}
NumStackPtr NumInitStack(void)
{
NumStackPtr S=(NumStackPtr)malloc(sizeof(NumStack));
S->bottom=S->top=(NumStackNodePtr)malloc(sizeof(NumStackNode));
if(S->bottom==NULL)exit(OVERFLOW);
else return S;
}
CharStackPtr CharInitStack(void)
{
CharStackPtr S=(CharStackPtr)malloc(sizeof(CharStack));
S->bottom=S->top=(CharStackNodePtr)malloc(sizeof(CharStackNode));
if(S->bottom==NULL)exit(OVERFLOW);
else return S;
}
Status NumGetTop(NumStackPtr S,int *e)
{
if(S->bottom==S->top) return ERROR;
*e=S->top->data;
return OK;
}
Status CharGetTop(CharStackPtr S,char *e)
{
if(S->bottom==S->top) return ERROR;
*e=S->top->data;
return OK;
}
Status NumPush(NumStackPtr S,int e)
{
NumStackNodePtr pNew=(NumStackNodePtr)malloc(sizeof(NumStackNode));
pNew->data=e;
pNew->next=S->top;
S->top=pNew;
return OK;
}
Status CharPush(CharStackPtr S,char e)
{
CharStackNodePtr pNew=(CharStackNodePtr)malloc(sizeof(CharStackNode));
pNew->data=e;
pNew->next=S->top;
S->top=pNew;
return OK;
}
Status NumPop(NumStackPtr S,int *e)
{
if(S->bottom==S->top) return ERROR;
*e=S->top->data;
NumStackNodePtr p=S->top;
S->top=S->top->next;
free(p);
p=NULL;//避免野指针
}
Status CharPop(CharStackPtr S,char *e)
{
if(S->bottom==S->top) return ERROR;
*e=S->top->data;
CharStackNodePtr p=S->top;
S->top=S->top->next;
free(p);
p=NULL;//避免野指针
}
Status isOPTR(char c)
{
if(c=='+'||c=='-'||c=='*'||c=='/'||c=='('||c==')'||c=='#')
return TRUE;
else return FALSE;
}
char Precede(char c1,char c2)
{
switch(c1)
{
case '+':
case '-':
if(c2=='+'||c2=='-'||c2==')'||c2=='#')
return '>';
else
return '<';
case '*':
case '/':
if(c2=='(')
return '<';
else
return '>';
case '(':
if(c2==')')
return '=';
else
return '<';
case ')':
return '>';
case '#':
if(c2=='#')
return '=';
else
return '<';
default:
printf("格式错误,吃屎去吧!\n");
return ERROR;
}
}
int Operate(int a,char sign,int b)
{
switch(sign)
{
case '+':
return a+b;
case '-':
return a-b;
case '*':
return a*b;
case '/':
return a/b;
}
}
int NewNumber(int a,int b)
{
return a*10+b;
}
void EvaluateExpression(int *n)
{
NumStackPtr OPND=NumInitStack();
CharStackPtr OPTR=CharInitStack();
CharPush(OPTR,'#');
char c=getchar();
int flag=0; char temp1; CharGetTop(OPTR,&temp1); int temp; char sign;
int a,b;
while(c!='#'||temp1!='#')
{
if(!isOPTR(c))
{
if(flag)
{
NumPop(OPND,&temp);
NumPush(OPND,NewNumber(temp,c-'0'));
c=getchar();
continue;
}
flag=1;
NumPush(OPND,c-'0');
c=getchar();
}
else
{
flag=0; CharGetTop(OPTR,&temp1);
switch(Precede(temp1,c))
{
case '<':
CharPush(OPTR,c);
c=getchar();
break;
case '=':
CharPop(OPTR,&temp1);
c=getchar();
break;
case '>':
CharPop(OPTR,&sign);
NumPop(OPND,&a);
NumPop(OPND,&b);
NumPush(OPND,Operate(a,sign,b));
break;
}
}
CharGetTop(OPTR,&temp1);
}
NumGetTop(OPND,&temp);
*n=temp;
}