栈(stack)又名堆栈,它是一种运算受限的线性表。
栈的限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
我们正是利用栈的这种特性来实现表达式求值,这里我要说的是中缀表达式求值。对于表达式求值的运算需要注意的一大问题就是运算优先级的比较,我们利用两个栈将操作数和运算符分开存放,当第一次遇到数字时我们将其压入栈低然后继续向后获取表达式的值,当遇到第一个符号时我们也将其压入栈低,不过在符号栈的初始化的过程中,我们应当首先向其中存入一个参考的符号 ‘#’ ,这个符号的总用是判断符号栈是否为空,这样做是因为我们输入的表达式的最后也会有这样一个字符 ‘#’ ,当两个 ‘#’相遇的时候则说明该表达式计算结束。
接下来是表达式计算的问题,当我们从表达式中取到的运算符比运算符栈顶的运算符优先级低的时候,则将栈定运算符取出,并取出两个操作数进行运算,当运算完成之后,再将运算结果写回栈定。
总的说来,就是首先比较两个运算符的优先级,然后根据优先级来决定是进行计算还是存入栈中。当然我们首先要进行优先级的比较。
优先级比较
我们将所有的运算符的比较结果存入一个二维数组,当我们需要比较两个运算符优先级高低的时候,我们只需要得到该二维数组对应的元素即可。比较结果如下表:
{'>','>','<','<','<','>','>'},
{'>','>','<','<','<','>','>'},
{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},
{'<','<','<','<','<','=','0'},
{'>','>','>','>','0','>','>'},
{'<','<','<','<','<','0','='},
7行7列分别代表的是+、-、、/、(、)、# 这些运算符,对于相同的运算符相遇+、-、、/ 默认前面的优先级高于后面的运算符,当两个左括号相遇则前括号优先级小于后括号,就相当于内部的括号。当两个右括号相遇,前括号优先级高于后括号。当两个#相遇说明计算已完成,当左右括号相遇则视之为相等,直接去括号即可,其余为’0’的情况是不可能出现的情况,即输入表达式错误。
具体计算过程涉及到出栈、入栈、得到栈顶元素、以及计算两个运算数的结果,下面是各部分的代码:
取得栈定元素
int get_stack(StackType * top, int * x)
{
if(top->next == NULL)
{
return -1;
}
*x = top->next->data;
return 0;
}
表达式求值
int EvaluateExpression()
{
int n;
int c; //存储输入缓存中的字符或数字
int flag; //从输入缓存中取操作符的返回值,0表示取出数字,1表示取出运算符
int x; //取栈顶操作符
int operate; //存储要计算的操作符
int a, b; //存取要计算的操作数
StackType * top_oprd, * top_optr; //top_oprd为操作数栈,top_optr为运算符栈
char op[] = "+-*/()#";
top_oprd = Init_stack();
top_optr = Init_stack();
Push_stack(top_optr, '#');
flag = getnext(&c);
get_stack(top_optr, &x);
while(c != '#' || x != '#') //表达式的起始位置都是'#',如果读取的新的字符和运算符都是'#'说明运算已经结束
{
if(flag == 0) //返回数字
{
Push_stack(top_oprd, c);
flag = getnext(&c);
}
else //返回运算符
{
get_stack(top_optr, &x); //取栈顶运算符
switch(compare(x, c))
{
case '<': //栈顶操作符运算优先级低
Push_stack(top_optr, c);
flag = getnext(&c);
break;
case '>': //栈顶运算符优先级高,先退出两个数据进行运算,然后将运算结果在存入数据栈
pop_stack(top_optr, &operate);
pop_stack(top_oprd, &a);
pop_stack(top_oprd, &b);
Push_stack(top_oprd, Operation(a, operate, b));
break;
case '=': //操作符'('')'紧挨,则直接去除括号
pop_stack(top_optr, &operate);
flag = getnext(&c);
break;
case '0': //比较结果得出表达式错误
printf("input error!\n");
}
}
get_stack(top_optr, &x);
}
get_stack(top_oprd, &c);
return c;
}
实际计算两个操作数的结果
int Operation(int a, char operate, int b) //实际操作两个数a,b的运算
{
int i, j, result;
i = a;
j = b;
switch(operate){
case '+': result = a + b; break;
case '-': result = a - b; break;
case '*': result = a * b; break;
case '/': result = a / b; break;
}
return result;
}
优先级比较函数
/*对于连续出现的运算符进行比较优先级
* 结果有> < =,可以得到+-×/之间的优
* 先级,加减乘除的优先级都低于'(',
* 但是都高于')',并且根据从左到右运算
* 可知当运算符相同时,第一个大于第二
* 个,0表示不存在
* */
char compare(char a, char b)
{
int i, j;
char pre[7][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;
}
return pre[i][j];
}
取表达式中下一个操作元素(数或运算符)
在这部分使用到了ungetc()函数,该函数是将刚读出的字符串重新写回输入缓存当中等待下一次读取。在此处的作用:当读到第一个字符为数字时继续向下一个读取,将连起来的数字符组成一个十进制数,但是这样势必会读到数字之后的一个字符,当读到该字符时结束,并将该非数字字符写回输入缓冲区中,等待下一次读取。
int getnext(int * n) //该函数返回0为数字,返回1为运算符
{
char c;
*n = 0;
while((c = getchar()) == ' '); //跳过空格
if(!isdigit(c)){ //判断是否是数字
*n = c;
return 1;
}
do{
*n = *n * 10 + (c - '0'); //使用循环获得连续的数字,乘10进位
c = getchar();
}while(isdigit(c));
ungetc(c, stdin); //读到一个运算符,将运算符写回输入缓存
return 0;
}
元素出栈
int pop_stack(StackType * top, int * x)
{
StackType * p;
if(top->next == NULL){
return -1;
}
p = top->next;
top->next = p->next;
*x = p->data;
free(p);
return 0;
}
元素入栈
int Push_stack(StackType * top, int x)
{
StackType * p;
p = (StackType *)malloc(sizeof(StackType));
if(p == NULL){
return FALSE;
}
p->data = x;
p->next = top->next;
top->next = p;
return TRUE;
}
栈的初始化
StackType * Init_stack()
{
StackType * top;
top = (StackType *)malloc(sizeof(StackType));
top->next = NULL;
return top;
}
详细的实现代码可以查看这里.