您的位置:首页 > 其它

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

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

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

先左后右:从左算到右
先乘除,后加减
先括号内,后括号外

原理:

使用两个栈来存储读入的字符:数字栈和符号栈
读入数字,则压入数字栈
读入符号,则把当前符号栈顶的符号与读入的符号进行优先级比较。若当前符号栈顶符号的优先级小,则继续把符号压入符号栈;若当前读入符号的优先级小,则数字栈依次出栈两个数,加上符号栈顶的符号,构成一算术表达式,通过计算得出结果,把结果压入数字栈。
当读入界限符或符号栈弹出界限符,如#,运算结束,数字栈弹栈,即最后的表达式值。

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

+-*/()#
+1122211
-1122211
*1111211
/1111211
(2222230
)1111011
#2222203
每一个位置可以用(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;
}


运行:计算 1+2-3*(3-5)



说明:可以使用栈的实现:顺序栈中的代码,但这里为了方便,减少代码量,使用#include<stack>,其实效果是一样的。以上程序,只是完成了简单的运算。说它简单是因为:只能输入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;
}

运行: -4+34%3+5*(3-5)



大家可以测试一下其它的表达式。不过还有点问题:那就是除法,用的是计算机中整数的除法,得到的只是整数,为什么不可以使用实数的除法,这点还有待改进。
代码下载
栈的应用:解析算术表达式

转载请注明出处,本文地址:/article/1378997.html

若是对你有所帮助,或是觉得有意思,希望顶一个哦。

专栏目录:

数据结构与算法目录
c指针
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: