数据结构之栈的应用----算术表达式的实现

这次所讲的是又一个栈的应用:算术表达式

这个程序是栈的应用的典型例子,是很好的栈的诠释,这个程序在K&R的书上也被叫做逆波兰计算器


/**********算术表达式的设计思想***********/

(1)首先,根据四则运算表达式,我们要先了解到其计算规则:括号为界限符,其优先率最高,其次为运算符*和/,最后为+和-;

(2)然后,我们根据把一个算术表达式划分为:开始('#')、运算符(加减乘除)、操作数(数值)和界限符(一对原括号)的方法,创建两个栈,

一个为Operator(运算符)栈,另一个为Operand(操作数)栈

<1>先将'#'push到Operator中,它将作为算术表达式的开始和结束标志

<2>我们依次把算术表达式的字符串给push如栈中,遇到数字时,就把它push到Operand中,这个比较好做,但是当遇到运算符时,我

们需要几个判断,后面会有介绍

<3>原括号的特殊处理(后面会说到)

<4>最后想说的是,我是先将整个表达式置于一个字符串中,整个计算的条件就是通过判断这个字符串有没有结束,如果到了最后,再通过

最开始我所说的'#'开始标志作为表达式的结束标志,来计算最后的结果(这是一个特殊处理,后面会说)


/************程序实现的几个细节***********/

我们这样就已经把这个程序的大致框架给描绘出来了,下面来说说实现这个程序的时候需要注意到的一些细节:

(栈的基本功能的实现就飘过了~~~)

(1)首先是运算符要被push或pop时的一些比较。

这是整个程序设计中最关键的步骤之一,我们给这个功能命名为Judge_Operator。有了最开始基本设计思想的讲述,这段代码应该比

较好实现了,这里要注意到是,我并没有把'('或着')'纳入判断中,因为我把原括号视为了一种特殊字符,当有圆括号出现时,就应该让

原括号里面的表达式独立出来,也就是让这个表达式单独进入一个新的循环进行这个表达式的求值,直到原括号消失在整个算术表达

式中,具体方法是这样的:

运算符中当是'('时,就表明其后面的必定有其他运算符(当然,不包括用户输入错误的情况,这里不做异常处理,能力有限啊。。。)

所以,我们需要把'('push到Operator中,并且其后的一个运算符也应该被push;当遇到')'就表明有一对括号已经出现,我们就需要把这一对

括号里的表达式计算出来,并且把'('给pop掉至于括号里表达式的计算请看下面的代码,由于还要考虑到括号嵌套的处理,所以我用了循环

来实现:

//'(' and ')' is a special symbol
if(expression[i] == '(')
{
	Push_Stack(&Operator,expression[i]);
}
else if(expression[i] == ')')
{
	//')' is a symbol to end a part of expression,value the result and push it to Oprand Stack
	GetElem_Stack(&Operator,&TopElemOptr);
	while(TopElemOptr != '(')
	{
		Pop_Stack(&Operator,&PopElemOptr);
		Pop_Stack(&Operand,&PopElemOpnd1);
		Pop_Stack(&Operand,&PopElemOpnd2);
		Push_Stack(&Operand,value(PopElemOpnd1,PopElemOpnd2,PopElemOptr));
		GetElem_Stack(&Operator,&TopElemOptr);
	}
	if(TopElemOptr == '(')
	{
		Pop_Stack(&Operator,&PopElemOptr);
	}
}
从代码中,可以知道处理括号嵌套的情况是用循环解决的,每一次都要判断栈顶元素是否是'(',我们只在if语句中考虑了')',并没有

考虑它出现次数,因为只要用户输入正确,有一个'('就必有一个')'剩下,所以只管利用'('判断括号是否还存在于整个表达式中,当循

环完毕之后,还剩下了一个'(',我们就需要人工地给它pop掉

(2)表达式结束的判断标识:'\0'和'#'

前面提过我将表达式存在了一个字符串中,当然得先以'\0'判断字符串结束没有,但是当字符串结束时,表达式的计算往往还没有结

束,字符串的结束只是代表着表达式被算了一次,也就是把相邻运算符的优先级相同的给合并了,但是还有不同的留在两个栈中,我

用来作为表达式计算开始标志的'#'作为表达式结束的标识,每计算一个小的表达式,就会pop一个运算符,到最后,肯定会只剩下一

开始就被push的'#',同样利用解决括号嵌套问题的方法来解决这个问题即可。

(3)相邻表达式的计算

这是被切成若干部分的小表达式的计算,它的关键就是通过Judge_Operator函数返回的每两个相邻运算符的优先级比较,运算符遇到

'*'或者'/'时,如果Operator的栈顶元素是'+'或者'-',就表明'*'或者'/'应该被push,如果也是'*'或者'/',也应该把它push,但是在push之前,

应该把栈顶的'/'或'*'弹出,再连续pop出两个Operand中的元素,这三个元素被弹出来再被计算出一个新值,push到操作数栈中,如果

是'(',那么就直接将'*'或者'/'push;如果运算符遇到'+'或者'-',很明显,只有遇到'('时才把它push,遇到其他的运算符,都应该把前面的

表达式计算出来再把它push,我的函数实现里是以返回'<'、'>'和'='来表示的,是一样的。


/********************特别说明*************************/

这里说明一下,这个程序我只实现了整数的算术表达式,源代码同样会放到我的代码共享里


/***********************Bug解决************************/

BUG1:

不能处理这种情况:开头就是    -2+5......等的表达式

解决:

(思想:将-2的表达式变为0.0-2即可)

只要在表达式不是数字及原括号的块里面首先判断:i == 0 &&expression[i] == '-'。

如果满足条件,就先在存放数字的栈中push一个0.0,再把'-'push到存放运算符的栈中。

BUG2:

不能处理这种情况:(-2)这种对负数有特殊要求时表达式

解决:

(思想:将(-2)变为(0.0-2))

同样,在上面解决BUG处的判断语句块后面加上专门解决这种情况的判断语句块。

判断条件:expression[i] == '-' && expression[i-1]== '('

如果满足条件,就0.0先push到存放数字的栈中,再把'-'push到运算符栈中

(解决上述两个BUG的前提是要采用我在上面将的设计思想,就是遇到一对原括号要先把这对括号里面的表达式给算出来再说)


你可能感兴趣的:(数据结构,框架)