一般来说,我们将表达式分为三种:前缀表达式、中缀表达式、后缀表达式。我们日常中使用最多的是中缀表达式。这里不在赘述这三种表达式的定义,有需要的读者可以参考博客前缀、中缀、后缀表达式(逆波兰表达式) - chensongxian - 博客园 。
栈的一个非常典型的应用,就是制作简单计算器,在计算器读入我们输入的中缀表达式时,并不会直接进行计算,通常会将它转变为后缀表达式,然后在计算后缀表达式的值。
接下来我们通过一个简单的例题,来练习栈和队列的应用。
题目:
读入一个包含 + - * / 四种运算符和括号的非负整数计算表达式,计算该表达式的值。
输入格式:
测试用例包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用空格分隔,括号和数字之间不用空格分隔。括号皆是英文输入法下的括号,没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
输出格式:
对每个测试用例输出一行,即该表达式的值,精确到小数点后2位。
输入样例:
30 / 90 - 26 + 97 - 5 - 6 - (13 / 88 * 6 + 51 / 29) + 79 * 87 + 57 - 92
985211 * 985 / 211
2 * (3 + 5 / 2)
0
输出样例:
12178.21
4599207.75
11.00
思路:
我们用string来接收输入的表达式。首先我们要对输入字符串进行去空格操作,以便于后续的处理。而处理的方法也很简单,我们遍历串,对出现空格的位置使用erase()方法即可。
接下来的算法包含两个步骤:
(1)中缀表达式转后缀表达式;
(2)计算后缀表达式。
步骤一:中缀转后缀-void change()函数
设立一个栈用于临时存放操作符;设立一个队列,用以存放后缀表达式。接着我们要按照如下定义运算符的优先级:乘号=除号>加号=减号,具体的实现可以用哈希表。然后我们从左至右开始遍历输入字符串,对于每一次遍历,按照如下情况处理:
(1)如果当前字符为数字,我们要考虑到数字可能不止一位,因此需要继续向后面遍历,将碰到的数字一位位合并,直到碰到操作符停止,将最后所得的操作数入队。例如123+4中,我们要将遍历得到的'1' '2' '3'合并成123。最后遍历继续至字符串下一位。
(2)如果当前字符为操作符op,比较其栈顶运算符的优先级。
a. op为左括号,则将op入栈,遍历继续至字符串下一位;
b. 若栈为空或者op的优先级大于栈顶元素的优先级,则将op入栈,遍历继续至字符串下一 位;
c. 若op的优先级小于等于栈顶元素,则将栈顶操作符出栈,并将其入队存放于后缀表达式; 之后我们的遍历停止一位,也就是继续保持当前操作符号op,待下一轮继续比较op与栈顶 元素的优先级
d. 若op为右括号,依次将栈顶元素出栈,并且将它们入队,直达栈顶元素为左括号停止。最 后将栈顶左括号出栈丢弃,遍历继续至字符串下一位。
待字符串遍历完成后,将栈内所有剩余操作符依次出栈,然后入队。
最后按顺序遍历队列,即可得到转换后的后缀表达式。
步骤二:后缀表达式的计算-double cal()函数
从左至右遍历后缀表达式(实际上就是队列出队的过程),对每一次遍历,按照如下情况处理:
a. 若当前为数字,将其压入栈。
b. 若当前为运算符op,弹出栈顶的两个数a和b,计算b op a,并将结果入栈;
待后缀表达式遍历完成后,栈内剩余唯一元素(即为栈顶元素),该元素为最终结果。
PS:上面描述了算法过程,而并未阐述其原理。读者不妨跟着参考博客中的举例来手动模拟这个算法,详细体会其过程。进而在这个过程中,慢慢理解其原理。前缀、中缀、后缀表达式(逆波兰表达式) - chensongxian - 博客园 (cnblogs.com) 。
代码如下:
#include#include #include



