在没有接触到栈这种数据结构时,一直觉得用程序计算四则表达式是件相当繁琐的事。但利用栈,问题就立刻变得容易许多。
何为解析算术表达式?
解析任何包括 + , - , * , / , () 和0到9数字组成的算术表达式,并计算其结果。
中缀表达式和后缀表达式
中缀表达式就是通常所说的算术表达式,比如(1+2)*3-4。
后缀表达式是指通过解析后,运算符在运算数之后的表达式,比如上式解析成后缀表达式就是12+3*4-。这种表达式可以直接利用栈来求解。
运算符的优先级
优先级 | 运算符 |
---|---|
1 | 括号 () |
2 | 负号 - |
3 | 乘方 ** |
4 | 乘 *,除 /,求余 % |
5 | 加 +,减 - |
6 | 小于<,小于等于<=,大于>,大于等于>= |
7 | 等于==,不等于!= |
8 | 逻辑与 |
9 | 逻辑或 |
即:一元运算符 > 二元运算符 > 多元运算符
解析过程
中缀表达式翻译成后缀表达式
- 从左向右依次取得数据a
- 如果 a 是数字,直接输出
- 如果 a 是运算符(含左右括号),则:
- 如果 a =’(‘,放入堆栈。
- 如果 a =’)’,依次输出堆栈中的运算符,直到遇到’(‘为止
- 如果 a 不是’)’或者’(‘,那么就和栈顶位置的运算符 top 做优先级比较
- 如果 a 优先级比 top 高,那么将 a 放入堆栈
- 如果 a 优先级低于或者等于 top,那么输出 top,然后将 a 放入堆栈
- 如果表达式已经读取完成,而栈中还有运算符时,依次由顶端输出
如果我们有表达式 (A-B)*C+D-E/F,要翻译成后缀表达式,并且把后缀表达式存储在一个名叫output的字符串中,可以用下面的步骤。
- 读取’(‘,压入堆栈,output为空
- 读取A,是运算数,直接输出到output字符串,output = A
- 读取’-‘,此时栈里面只有一个’(‘,因此将’-‘压入栈,output = A
- 读取B,是运算数,直接输出到output字符串,output = AB
- 读取’)’,这时候依次输出栈里面的运算符’-‘,然后就是’(‘,直接弹出,output = AB-
- 读取’*’,是运算符,由于此时栈为空,因此直接压入栈,output = AB-
- 读取C,是运算数,直接输出到output字符串,output = AB-C
- 读取’+’,是运算符,它的优先级比’‘低,那么弹出’‘,压入’+”,output = AB-C*
- 读取D,是运算数,直接输出到output字符串,output = AB-C*D
- 读取’-‘,是运算符,和’+’的优先级一样,因此弹出’+’,然后压入’-‘,output = AB-C*D+
- 读取E,是运算数,直接输出到output字符串,output = AB-C*D+E
- 读取’/’,是运算符,比’-‘的优先级高,因此压入栈,output = AB-C*D+E
- 读取F,是运算数,直接输出到output字符串,output = AB-C*D+EF
- 原始字符串已经读取完毕,将栈里面剩余的运算符依次弹出,output = AB-C*D+EF/-
计算算术表达式
当有了后缀表达式以后,运算表达式的值就非常容易了。可以按照下面的流程来计算。
- 从左向右扫描表达式,一个取出一个数据data
- 如果 data 是操作数,就压入堆栈
- 如果 data 是操作符,就从堆栈中弹出此操作符需要用到的数据的个数,进行运算,然后把结果压入堆栈
- 如果数据处理完毕,堆栈中最后剩余的数据就是最终结果
比如我们要处理一个后缀表达式1234+*+65/-,那么具体的步骤如下:
- 首先1,2,3,4都是操作数,将它们都压入栈
- 取得’+’,为运算符,弹出数据3,4,得到结果7,然后将7压入栈
- 取得’*’,为运算符,弹出数据7,2,得到数据14,然后将14压入栈
- 取得’+’,为运算符,弹出数据14,1,得到结果15,然后将15压入栈
- 6,5都是数据,都压入栈
- 取得’/’,为运算符,弹出数据6,5,得到结果1.2,然后将1.2压入栈
- 取得’-‘,为运算符,弹出数据15,1.2,得到数据13.8,这就是最后的运算结果
大部分参考自 chenpeggy 的利用堆栈解析算术表达式,收获颇丰,故有此文。