这个例子是书上P52页栈应用举例的其中一个算法
算法的简单描述
为了实现这个算法,我们需要两个栈,一个是符号栈OPTR,另一个是数据栈OPND,算符预先设定好优先级,当解析到数字的时候,将其入数据栈
当解析到运算符的时候,比较它和之前一个运算符的优先级,如果比之前的优先级高的话,先入栈,如果和之前的元素优先级一样的话,就可以将括号去掉了(因为这个时候已经算完了),如果比之前的优先级低的话,那么就需要将两个运算数据出栈,运算符也出栈,进行运算,然后将运算结果入栈,并且保留这个运算符继续进行比较。
这里有一个小细节需要注意,就是,先出栈的是第二个运算数,后出栈的是第一个运算数,如果没有注意到的话做减法的时候符号是相反的。
如果符号栈OPTR的栈底元素是#,并且当前元素也是#的话,表示解析结束
算符优先级表如下所示
代码实现
我是用查表的方式来检查两个算符的优先级关系的
用1表示theta1>theta2;用-1表示theta1<theta2;用2表示语法错误
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <stack>
#include <ctype.h>
using namespace std;
stack<char> OPTR; //初始化算符栈
stack<int> OPND; //初始化数据栈
//算符优先级表,1表示大于,0表示等于,-1表示小于,2表示不合法
//第一维是前一个算符,第二位是后一个算符
int priority[7][7] =
{{1, 1, -1, -1, -1, 1, 1},
{1, 1, -1, -1, -1, 1, 1},
{1, 1, 1, 1, -1, 1, 1},
{1, 1, 1, 1, -1, 1, 1},
{-1, -1, -1, -1, -1, 0 , 2},
{1, 1, 1, 1, 2, 1, 1},
{-1, -1, -1, -1, -1, 2, 0}};
//判断字符是不是运算符,如果是运算符返回1,不是运算符返回0
int isOP(char c){
if(c=='+' || c=='-' || c=='*' || c=='/' ||
c=='(' || c==')' || c=='#'){
return 1; //是运算符
} else {
return 0;
}
}
//查表程序,如果输入为3表示出错了
int precede(char a1, char a2){
int a=-1 , b=-1;
switch(a1){
case '+':
a = 0;
break;
case '-':
a = 1;
break;
case '*':
a = 2;
break;
case '/':
a = 3;
break;
case '(':
a = 4;
break;
case ')':
a = 5;
break;
case '#':
a = 6;
break;
default:
return 3; //出错
break;
}
switch(a2){
case '+':
b = 0;
break;
case '-':
b = 1;
break;
case '*':
b = 2;
break;
case '/':
b = 3;
break;
case '(':
b = 4;
break;
case ')':
b = 5;
break;
case '#':
b = 6;
break;
default:
return 3; //出错
break;
}
return priority[a][b];
}
//进行运算,返回运算的结果
int operate(int a, char theta, int b){
switch(theta){
case '+':
printf("%d+%d = %d\n",a, b, a+b);
return a+b;
case '-':
printf("%d-%d = %d\n",a, b, a-b);
return a-b;
case '*':
printf("%d*%d = %d\n",a, b, a*b);
return a*b;
case '/':
printf("%d/%d = %d\n",a, b, a/b);
return a/b;
default:
printf("the op is %c\n", theta);
return 0;
}
}
//解析整数值,返回时c存的是下一个运算符号
int getNumber(char &c){
int num = 0;
num += (c-'0');
c = getchar();
while(isdigit(c)){
//printf("%c", c);
num *= 10;
num += (c-'0');
c = getchar();
}
return num;
}
//解析表达式的主函数,返回解析得到的结果
int expression(){
OPTR.push('#'); //在开始的时候先将#入栈
char c;
c = getchar();
while(c!='#' || OPTR.top() != '#'){ //只有当两个都是#时才结束解析
//printf("%c", c);
if(c == ' '){ //略过空格
c = getchar();
continue;
}
if(!isOP(c)){ //不是运算符则入数据栈
int out = getNumber(c);
//printf("out = %d\n", out);
OPND.push(out);
} else { //是运算符
int a = precede(OPTR.top(), c);
//printf("a=%d\n", a);
if(a == -1){ //当前运算符优先级小于前一个运算符
OPTR.push(c);
c = getchar();
} else if(a == 0){ //运算符等级相等,直接消掉括号即可
OPTR.pop();
c = getchar();
} else if(a == 1){ //进行运算
char b = OPTR.top();
OPTR.pop();
int a1 = OPND.top();
OPND.pop();
int a2 = OPND.top();
OPND.pop();
int out = operate(a2, b, a1);
//printf("out = %d\n", out);
OPND.push(out);
//注意,需要将这个算符继续比较
} else if(a == 2){
printf("error\n");
}
}
}
return OPND.top(); //将最终的运算结果返回
}
int main(){
freopen("in.txt", "r", stdin);
printf("outcome = %d\n", expression());
}
测试用例(只有一个)
20 * (10 - 2)#