在某些情况下,我们需要对输入字符串表达式进行计算,例如一个字符串为:“1 + 2 * 3”,我们需要计算出它的结果,在这里,我提供李春葆老师书中所写的《数据结构教程》里的算法,这种算法精密之处让我惊叹,特此分享。
首先这个表达式只允许存在加减乘除和括号,但这也足够复杂了。
计算机是无法识别加减乘除的优先计算顺序的,更何况还有括号,这是更难以解决的。这时候必须修改这个表达式,使计算机能够明白它的计算顺序,其实很简单,提供一个后缀表达式就行,什么是后缀表达式,把上面“1 + 2 * 3”改为后缀表达式就是“2 3 * 1 +”,这样可以利用一个简单的算法就可以算出结果,这也就是把先需要计算的数据和运算符排在前面,后需要计算的数据和运算符排在后面。
怎么实现这个后缀表达式呢?这就是需要栈的应用。
我们首先创建一个栈,称这个栈为“运算栈”吧,压栈一个“=”,我们取这个“=”为0优先级(也就是最低优先级)。从字符串表达式中提取数据,如果是计算数据,则放在另一个表达式中(用来构成后缀表达式),每当遇到运算符的时候,就和左运算栈的栈顶运算符的优先级进行比较,如果外运算符(为了分别运算栈里的运算符,我称外面的运算符为外运算符)的优先级较高,这样就把这个外运算符压进运算栈中,相反如果外运算符的优先级较低,这样就把内运算符出栈,并将添加在上面说的后缀表达式中,最后,如果遍历完字符串表达式后,将运算栈的所有运算符出栈,添加在后缀表达式中,当然,“=”是不添加的。利用栈后进先出的特性,这样就把低优先级的运算符排在后面啦。
这里有两个问题:
1.如果遇到相等运算优先级的怎么办,如“+”遇到“+"或者“+”遇到“-”;
2.括号的优先级怎么处理。
首先解决第一个问题,遇到相等优先级的怎么办。我们知到,在现实中,相等优先级的运算符,谁在前面就谁先算,而在这里表现的是在运算栈里的运算符要先算,也就是,遇到相同优先级的运算符要将栈内运算符出栈添加在后缀表达式中,然后再将外运算符对栈顶的运算符进行优先级比较。这样也就解决了第一个问题。
第二个问题为括号的优先级要怎么处理。在现实计算中,括号里面的运算符都是要首先计算,这应该怎么处理呢,在李老师的书中提出了一个解决方法,让括号的优先级变化,让它在外运算符的优先级和在栈内的优先级不一样。我在这里详细的说一下,括号有左括号和右括号。当左括号在外运算符时,它的优先级必须最高,因为括号内的数据计算要隔离开外面的,这样,左括号在外运算符的优先级最高时,它就将会压栈,这样就和外面的运算符隔离开了。而左括号进入运算符后,它的优先级必须最低,这是因为如果保持最高优先级,这样另外的运算符跟它比较时,就无法入栈,只有最低优先级,才能保证括号内的运算符都能经过运算栈的处理。当右括号在外运算符的时候,它的优先级必须最低,这样就可以把经过运算栈处理过的运算符都能添加到后缀表达式中,当它跟运算栈内的左括号比较时,两个运算符都是最低优先级,这就尴尬了。我们在这里的处理应该是将左括号和右括号都抛弃掉,让它不存在在栈内和后缀表达式中,这样就和第一个问题的解决方法冲突了,因为在第一个问题解决方法中,相同优先级的运算符是应该讲运算栈内的运算符添加进后缀表达式,而外运算符和运算栈内的栈顶运算符比较。在这里,为了合理解决两个问题的方法,只有降低某些外运算符的优先级,这样外运算符的优先级低于运算栈内的运算符,可以使运算栈内的运算符添加进后缀表达式,但运算栈内左括号运算优先级应该和外运算符中的右括号优先级一样,这样就可以使两个优先级相同的左括号和右括号被抛弃。下面是李老师书中提供的运算符优先级:
运算符 = ( + - * / )
内运算符 0 1 3 3 5 5 6
外运算符 0 6 2 2 4 4 1
提供一个伪算法:
ch为字符串表达式中的字符。
while(ch != 0)
{
if(ch为运算数据)
{
while ( ch为运算数据)
{
将ch添加进后缀表达式;
ch++;
}
}
else
{
switch(ch和运算栈的栈顶运算符优先级)
{
case 小 :将ch压进运算栈中;
ch++;
break;
case 相等:将外运算符和栈顶运算符都抛弃掉;
ch++;
break;
case 大:栈顶运算符添加进后缀表达式;
break;
}
}
}最后将栈中的所有运算符出栈添加在后缀表达式中,‘=’不用添加。
这篇文章的Demo