您的位置:首页 > 其它

栈的应用:解析算术表达式

2016-10-04 09:36 330 查看
原博客:http://blog.csdn.net/zhangxiangDavaid/article/details/27176751

类似于 1*2-3+4-(5-6)-2/4 的式子,我们称为算术表达式。下面我们利用栈这种数据结构来解析它,即用栈来辅助计算算术表达式。

首先我们得明确计算规则:

先左后右:从左算到右

先乘除,后加减

先括号内,后括号外

原理:

使用两个栈来存储读入的字符:数字栈和符号栈

读入数字,则压入数字栈

读入符号,则把当前符号栈顶的符号与读入的符号进行优先级比较。若当前符号栈顶符号的优先级小,则继续把符号压入符号栈;若当前读入符号的优先级小,则数字栈依次出栈两个数,加上符号栈顶的符号,构成一算术表达式,通过计算得出结果,把结果压入数字栈。

当读入界限符或符号栈弹出界限符,如#,运算结束,数字栈弹栈,即最后的表达式值。

难点:

1、优先级数组priority的理解是难点。

+ - * / ( ) #

+ 1 1 2 2 2 1 1

- 1 1 2 2 2 1 1

* 1 1 1 1 2 1 1

/ 1 1 1 1 2 1 1

( 2 2 2 2 2 3 0

) 1 1 1 1 0 1 1

#2 2 2 2 2 0 3

每一个位置可以用(i,j)表示,其中i是行,j是列。有一点要明确:符号i是先于符号j的。也就是说,在算术表达式中符号i位于符号j的左边。1:>,2:<,3:=。界限符#的优先级是最低的。举几个例子说明:

(+,+)=1,两个’+’是同级的,但第一个先于第二个,由上述的计算规则1知:>。

((,#)=0,如果左括号’(‘出现了,至少右括号’)’出现了之后,才会有界限符,否则出错。

(),()=0,我们不支持(…)(…)这种写法,两括号之间至少要有其它的操作符才行,如(…)*(…)。

(#,))=0,没遇到左括号’(‘,怎能出现右括号’)’,故出错。

2、当符号栈顶的符号优先级高于当前读入的操作符时,数字栈出栈的第一个数字作为第二个操作数,下一个出栈的数字才作为第一个操作数,这一点得明白。operand:操作数 operator:操作符

细节看代码:

#include<iostream>
#include<stack>
using namespace std;

char OP[7] = {'+','-','*','/','(',')','#'};  //运算符集合
int priority[7][7] =     //各运算符相遇时,优先级比较 1:大于,2:小于,3:等于,0:不可能,错误
{
{ 1, 1, 2, 2, 2, 1, 1 },
{ 1, 1, 2, 2, 2, 1, 1 },
{ 1, 1, 1, 1, 2, 1, 1 },
{ 1, 1, 1, 1, 2, 1, 1 },
{ 2, 2, 2, 2, 2, 3, 0 },
{ 1, 1, 1, 1, 0, 1, 1 },
{ 2, 2, 2, 2, 2, 0, 3 }
};
bool isOpat(char c)   //是否是OP[]中的操作符
{
for (int i = 0; i < 7; i++)
{
if (c == OP[i])
return true;
}
return false;
}
int getPriority(char c1,char c2)  //比较优先级
{
int i, j;
for (int r = 0; r < 7; r++)
{
if (c1 == OP[r])
i = r;
if (c2 == OP[r])
j = r;
}
return priority[i][j];
}
int compute(char a, char op, char b)
{
switch (op)
{
case '+':
return (a - '0') + (b - '0');
case '-':
return (a - '0') - (b - '0');
case '*':
return (a - '0')*( b - '0');
case '/':
if (b == '0')
{
cout << "错误!" << endl;
exit(0);
}
return (a - '0')/(b - '0');
}
}
void evaluateExpression()   //计算
{
stack<char> opan,opat;    //构建两个栈 operand:操作数,operator:操作符
opat.push('#');    // # 压入符号栈,作为界限符
cout << "输入算术表达式" << endl;
char op,a,b,c;
c=getchar();
while (c != '#' || opat.top() != '#')  //没有读到 '#',或者符号栈也没空,则继续读取字符
{
//对读入的字符进行判断:是操作数还是操作符?
if (!isOpat(c))  //是操作数则压入操作数栈
{
opan.push(c);
c = getchar();
}
else   //若是操作符,则需把符号栈顶的操作符与当前读入的操作符,进行优先级比较
{
switch(getPriority(opat.top(), c))
{
case 1:
op = opat.top(); opat.pop();
b = opan.top(); opan.pop();
a = opan.top(); opan.pop();
opan.push(char(compute(a,op,b)+'0'));
break;
case 2:
opat.push(c);
c = getchar();
break;
case 3:
opat.pop();
c = getchar();
break;
case 0:
cout << "错误!" << endl;
exit(0);
}
}
}
cout << "= " << opan.top()-'0' << endl;
}
int main()
{
cout << "使用栈结构解析计算表达式"<<endl;
evaluateExpression();
system("pause");
return 0;
}


说明:可以使用栈的实现:顺序栈中的代码,但这里为了方便,减少代码量,使用#include,其实效果是一样的。以上程序,只是完成了简单的运算。说它简单是因为:只能输入0-9的数字,并且不能处理以符号开头的表达式,如-3*4,+2-5,则出错。后续补充增强版,能处理诸如 34+34,多位数的运算。

update 2014-5-28 00:28

这次更新,解决了上述问题,并添加了求余%运算符。代码如下:

#include<iostream>
#include<stack>
using namespace std;
char OP[8] = {'+','-','*','/','%','(',')','#'};  //运算符集合
int priority[8][8] =   //各运算符相遇时,优先级比较 1:大于,2:小于,3:等于,0:不可能,错误
{
{ 1, 1, 2, 2, 2, 2, 1, 1 },
{ 1, 1, 2, 2, 2, 2, 1, 1 },
{ 1, 1, 1, 1, 1, 2, 1, 1 },
{ 1, 1, 1, 1, 1, 2, 1, 1 },
{ 1, 1, 1, 1, 1, 2, 1, 1 },
{ 2, 2, 2, 2, 2, 2, 3, 0 },
{ 1, 1, 1, 1, 1, 0, 1, 1 },
{ 2, 2, 2, 2, 2, 2, 0, 3 }
};
bool isOpat(char c)   //是否是OP[]中的操作符
{
for (int i = 0; i < 8; i++)
{
if (c == OP[i])
return true;
}
return false;
}
int getPriority(char c1,char c2)  //比较优先级
{
int i, j;
for (int r = 0; r < 8; r++)
{
if (c1 == OP[r])
i = r;
if (c2 == OP[r])
j = r;
}
return priority[i][j];
}
int compute(int a, char op, int b)
{
switch (op)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a*b;
case '/':
case '%':
if (b == 0)
{
cout << "错误!" << endl;
exit(0);
}
if (op == '/')
return a / b;
else
return a%b;

}
}
void evaluateExpression()   //计算
{
stack<int> opan;     //操作数栈
stack<char> opat;    //操作符栈
opat.push('#');    // # 压入符号栈,作为界限符
cout << "输入算术表达式" << endl;
char op,c,cn;    //cn:char next下一个字符
int a,b,data;
c=getchar();
if (isOpat(c))   //如果第一个字符就是操作符,则把0压入数字栈
opan.push(0);
while (c != '#' || opat.top() != '#')  //没有读到 '#',或者符号栈也没空,则继续读取字符
{
//对读入的字符进行判断:是操作数还是操作符?
if (!isOpat(c))  //是操作数则压入操作数栈
{
data = c - '0';
while (!isOpat(cn = getchar()))   //下一个字符任为数字
{
data = data * 10 + cn - '0';
}
opan.push(data);
c = cn;   //把cn中的操作符赋给c
}
else   //若是操作符,则需把符号栈顶的操作符与当前读入的操作符,进行优先级比较
{
switch(getPriority(opat.top(), c))
{
case 1:
op = opat.top(); opat.pop();
b = opan.top(); opan.pop();
a = opan.top(); opan.pop();
opan.push(compute(a,op,b));
break;
case 2:
opat.push(c);
c = getchar();
break;
case 3:
opat.pop();
c = getchar();
break;
case 0:
cout << "错误!" << endl;
exit(0);
}
}
}
cout << "= " << opan.top() << endl;
}
int main()
{
cout << "使用栈结构解析计算表达式"<<endl;
evaluateExpression();
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: