1.涉及概念
1.1 栈
栈是一种操作受限的线性表,即只允许从一端插入和删除数据,他的存储方式分为线性存储和链接存储。栈的一个最重要的特征就是栈的插入和删除只能在栈顶进行,所以每次删除的元素都是最后进栈的元素,故栈也被称为后进先出(LIFO)表。栈主要有两种操作,分为入栈(push)和出栈(pop)。由于只操作栈顶元素,所以此操作的复杂度为O(1)。
1.2 逆波兰表达式
逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如(a+b)*(c+d)转换为ab+cd+*。如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
2.前提说明
本篇文章重点是说明四则运算转换成逆波兰表达式,最后计算表达式结果,所以本次只考虑四则运算。接下来,我们会以一个例子讲解运算过程。
四则运算表达式:
3 + 4 * (5 + 6 / 7) / (8 - 9) - 10 + 11
3.四则运算表达式转换为逆波兰表达式
3.1 总体思路
从左到右遍历中缀表达式的每个数字和符号,若是数字则直接输出,若是符号,则判断其于栈顶符号的优先级,是有括号或者优先级低于栈顶符号,则栈顶元素依次出栈并输出,直到遇到左括号或栈空才将之前那个符号入栈。
3.2 变量声明
为说明方便简单,这里设定逆波兰表达式为exp,符号栈为stack。以下每一步骤处理一个字符。
3.2 具体步骤
01)"3"为数字,所以输出3。此时exp为{3};stack为{}。
02)"+"为符号,并且栈顶为空,所以压入栈顶。此时exp为{3};stack为{+}。
03)"4"为数字,所以输出4。此时exp为{3 4} ;stack为{+}。
04)"*"为符号,此时栈顶元素为"+",并且优先级小于"*",所以"*"压人栈顶。此时exp为{3 4},stack为{+ *}。
05)"("为符号,并且为左括号,所以"("压人栈顶。此时exp为{3 4},stack为{+ * (}。
06)"5"为数字,所以输出5。此时exp为{3 4 5} ;stack为{+ * (}。
07)"+"为符号,此时栈顶元素为"(",所以"+"压人栈顶。此时exp为{3 4 5 },stack为{+ * ( +}。
08)"6"为数字,所以输出6。此时exp为{3 4 5 6} ;stack为{+ * ( +}。
09)"/"为符号,此时栈顶元素为+,优先级低于"/",所以"/"压入栈顶。此时exp为{3 4 5 6} ;stack为{+ * ( + /}。
10) "7"为数字,所以输出7。此时exp为{3 4 5 6 7} ;stack为{+ * ( + /}。
11)")"为符号,并且为右括号。所以依次将栈内左括号"("以前的运算符号依次弹出。此时exp为{3 4 5 6 7 / +};stack为{+ *}。
12)"/"为运算符号,此时栈顶元素为"*",优先级等于"/",所以弹出栈顶元素"*;接下来,此时栈顶元素为"+",优先级低于"/",所以压入栈顶。此时exp为{3 4 5 6 7 / + *};stack为{+ /}。
13)"("为符号并且为左括号,所以压入栈顶。此时exp为{3 4 5 6 7 / + *};stack为{+ / (}。
14)"8"为数字,所以输出8。此时exp为{3 4 5 6 7 / + * 8};stack为{+ / (}。
15)"-"为符号,栈顶元素为"(",所以直接压入栈顶。此时exp为{3 4 5 6 7 / + * 8};stack为{+ / ( -}。
16)"9"为数字,所以输出9。此时exp为{3 4 5 6 7 / + * 8 9};stack为{+ / ( -}。
17)")"为符号且为有括号,将栈中左括号以前的运算符弹出栈。此时exp为{3 4 5 6 7 / + * 8 9 -};stack为{+ / }。
18)"-"为运算符号,此时栈顶元素为"/",优先级大于"-",所以弹出栈顶元素"/";此时栈顶元素为"+",优先级等于"-",所以弹出栈顶元素"+",同时将"-"压如栈顶。此时exp为{3 4 5 6 7 / + * 8 9 - / +};stack为{- }。
19)"10"为数字,所以输出10。此时exp为{3 4 5 6 7 / + * 8 9 - / + 10};stack为{- }。
20)"+"为运算符号,此时栈顶元素为"-",优先级等于"+",所以弹出栈顶元素,同时将"+"压如栈顶。此时exp为{3 4 5 6 7 / + * 8 9 - / + 10 -};stack为{+ }。
21)"11"为数字,所以输出11。此时exp为{3 4 5 6 7 / + * 8 9 - / + 10 - 11};stack为{+ }。
22)将栈内元素依次弹出。所以最终的exp为{3 4 5 6 7 / + * 8 9 - / + 10 - 11 + }。
4.基于栈的逆波兰表达式运算
4.1 总体思路
遍历逆波兰表达式的每个元素,如果为数字,则压入栈顶;如果是符号,则弹出栈顶两个元素,将数据进行运算,得到的数据压入栈顶。
4.2 前提
通过上面计算的表达式为{3 4 5 6 7 / + * 8 9 - / + 10 - 11 + }。
4.3 变量声明
为说明方便简单,这里涉及到一个栈,我们定义为stack.
4.4 计算步骤
1)3为数字,压入栈顶。此时stack为{3}。
2)4为数字,压入栈顶。此时stack为{3 4}。
3)5为数字,压入栈顶。此时stack为{3 4 5}。
4)6为数字,压入栈顶。此时stack为{3 4 5 6}。
5)7为数字,压入栈顶。此时stack为{3 4 5 6 7}。
6)"/"为符号,此时弹出栈顶两个元素为6和7,所以将6和7做"/"运算,得到数据为0.857。并将0.857压入栈顶。
此时stack为{3 4 5 0.857}。
7)"+"为符号,此时弹出栈顶两个元素为5和0.857,所以将5和0.857做"+"运算,得到数据为5.857。并将5.857压入栈顶。
此时stack为{3 4 5.857}。
8)"*"为符号,此时弹出栈顶两个元素为4和5.857,所以将4和5.857做"*"运算,得到数据为23.428。并将23.428压入栈顶。
此时stack为{3 23.428}。
9)8为数字,压入栈顶。此时stack为{3 23.428 8}。
9)9为数字,压入栈顶。此时stack为{3 23.428 8 9}。
10)"-"为符号,此时弹出栈顶两个元素为8和9,所以将8和9做"-"运算,得到数据为-1。并将-1压入栈顶。
此时stack为{3 23.428 -1}。
11)"/"为符号,此时弹出栈顶两个元素为23.428和-1,所以将23.428和-1做"/"运算,得到数据为-23.428。并将-23.428压入栈顶。
此时stack为{3 -23.428}。
11)"+"为符号,此时弹出栈顶两个元素为3和 -23.428,所以将3和 -23.428做"+"运算,得到数据为-20.428。并将-20.428压入栈顶。
此时stack为{-20.428}。
12)"10"为数字,压入栈顶。此时stack为{-20.428 10}。
13)"-"为符号,此时弹出栈顶两个元素为-20.428和10,所以将-20.428和10做"-"运算,得到数据为-30.428。并将-30.428压入栈顶。
此时stack为{-30.428}。
14)"11"为数字,压入栈顶。此时stack为{-30.428 11}。
15)"+"为符号,此时弹出栈顶两个元素为-30.428和11,所以将-30.428和11做"+"运算,得到数据为-19.428。并将-19.428压入栈顶。
此时stack为{-19.428}。
16)逆波兰表达式遍历完毕,此时栈顶的数据即为计算的结果。为-19.428。
17)我们通过计算器计算得到的结果为:-19.429