之前在学习栈的时候老师讲过这个问题
思路就是:
1.将表达式(中缀式)转化成后缀式;
2.进行后缀式的计算。
思路看起来很简单,但是实际在敲代码的时候还是要注意很多问题。下面分享一下个人做法,可以改进之处还希望大家可以指出来,共同进步!
一:将中缀式转化为后缀式
个人采用的是边输入边进行处理:首先设置两个栈,一个是后缀式栈,一个是符号栈。如果是数字,直接放入到后缀式栈中;如果是符号:
(1)比较算符优先级,如果符号栈的栈顶符号比将要进栈的符号优先级高,那么将符号栈的栈顶元素弹出,进入到后缀式栈中,接着进行栈顶元素和待进栈元素的比较,直到栈顶元素优先级比待进栈符号优先级低。如果符号栈的栈顶符号优先级低,则将将要进栈的符号压入到符号栈中,称为新的栈顶元素。以上涉及到了:算符优先级的比较,实现代码如下:
紫框详解:我们知道,‘(’的优先级是最高的,那么如果它作为栈顶元素与其他符号比较之后,会被弹出到后缀式栈。然而我们后缀式栈不希望有括号的出现,而符号栈希望括号可以保留,因为我们需要找到后面的‘)’与左括号进行配对。所以这里需要约定,如果栈顶元素是左括号,那么下一个符号直接进栈不进行比较。
以上是我们后缀式转化的代码。在suffix函数(转化成后缀式)中,我们是边输入边处理,如果是数字,直接进栈,调用isNum函数;如果是符号,调用isSymbol函数。
在isSymbol函数中,我们做的不仅仅是优先级的判断,因为有括号这个特殊的符号存在,所以我们需要给予特殊考虑。在遇到右括号的时候,我们应该将符号栈中的元素弹出,直到遇到左括号,但是左括号不弹入到后缀式栈中。
如果不是右括号,我们就判断优先级,但是在这之前,还需要做的一步就是设置分界符,我们这里使用的是'#'号。为什么设置分界符呢?我们知道,读入表达式的时候使用的是char型,一个一个读的,这样产生的问题是,如果数字是10以内的,没有影响,但是如果大于等于10,则会分成多部分读入,例如23读进来是2,3,在不同的数组单元中,那么操作数字的时候就不知道到底是一位数还是两位数,所以我们需要‘#’号,后缀式的形式就成了:1#23#4#+......这样子的,很容易区分操作数是几位。
我第一次做的时候,忽略了一个问题:就是将符号栈的剩余元素全部压入到后缀式栈中。因为在优先级比较之后,很多符号可能因为在操作顺序中靠后,所以并没有进入到后缀式中,因此千万不能忘了他们的存在。
到这里,我们的后缀式转换就完成了。工程相当于完成了一半,接下来就应该是后缀式的计算:
前面说到过了,我们后缀式中通过'#'号对数字进行了区分,那么我们进行计算的时候,先要把操作数准确的计算出来,放入到数字栈(num[])中。
红框详解:对于转之后的后缀式,计算操作数我想到的办法是设置一个first和last指针,在first和last之间的是一个操作数,那么区分的标志就是,两个操作数之间一定有若干个符号。
大家再对照着代码,应该可以理解(可能这里我的方法比较麻烦,希望大家可以指点一二)
下面我们就开始进行计算了:
这里还是比较简单的,遇到数字就调用cul_num函数,进行数字的计算,遇到符号就进行符号运算。这里需要注意的就是我们first和last的设置。此外,还应该考虑一下特殊的情况,我当时出现的一个问题就是:
例如:1#2#3-#4...进行到‘-’减号的时候程序就崩掉了,后来想想明白了问题所在,当last指向减号的时候,执行减法功能,然后i++,那么这个时候,指向的是后缀式栈中的‘#‘号,而在calculate函数中,没有对#的相关处理,所以程序崩了。分析得到:我们并不需要#,所以再i++,指向后缀式栈中的下一个元素。这就是我上面代码注释的“忽略了”这里的含义。
(本人第一次写博客,不到之处还希望大家批评指正,我会继续努力!共勉!)