在程序設計中,可能碰到須要對字符串數學表達式求值的問題,經常使用的方法是解析表達式,生成二叉樹,而後進行計算。編譯器就是使用這種方法來解析程序中的表達式的。這種方法實現起來有點難度,須要考慮運算符的優先級,括號的配對,堆棧的使用等等。咱們正常狀況下看到的數學表達式若是用二叉樹遍歷的話,剛好是中序遍歷,故叫作中序表達式。除此以外,還有前序表達式,後序表達式。如:a+b+c(中序),++abc(前序),ab+c+(後序),若是表達式含有×,/,()等就更復雜了。php
後綴表達式也稱逆波蘭表達式 因其使表達式求值變得輕鬆,因此被廣泛使用。java
逆波蘭式:算法
在一般的表達式中,二元運算符老是置於與之相關的兩個運算對象之間(如:1+1),因此這種表示法也稱爲中綴表示。波蘭邏輯學家J.Lukasiewicz於1929年提出了另外一種表示表達式的方法,稱爲逆波蘭記法,在逆波蘭記法中,全部操做符置於操做數的後面,所以也被稱爲後綴表示法。示例以下:express
中綴表示編程 |
逆波蘭式編程語言 |
a+b性能 |
a,b,+lua |
a+(b-c)spa |
a,b,c,-,+.net |
a+(b-c)*d |
a,b,c,-,d,*,+ |
a+d*(b-c) |
a,d,b,c,-,*,+ |
a=1+3 |
a=1,3 + |
逆波蘭表達式是一種十分有用的表達式,它將複雜表達式轉換爲能夠依靠簡單的操做獲得計算結果的表達式。它的優點在於只用兩種簡單操做,入棧和出棧就能夠搞定任何普通表達式的運算。
中綴表達式轉換爲逆波蘭式:
將一個普通的中序表達式轉換爲逆波蘭表達式的通常算法是:
一、首先構造一個運算符棧,此運算符在棧內遵循越往棧頂優先級越高的原則。
二、讀入一箇中綴表達式,爲了方便起見,可在其最右端追加一個最低優先級運算符(如:#號)。(這樣作的目的是,最後讀入#號運算符時將運算符棧中全部運算符都輸出)。
三、從左至右掃描該中綴表達式,若是當前字符是數字,則分析到該數字串的結束,並將該數字串直接輸出。
四、若是不是數字,該字符則是運算符,此時需比較該運算符與運算符棧頂運算符的優先關係:
(1)、若該運算符優先級高於棧頂運算符優先級別(或棧爲空),則直接將該運算符壓入運算符棧中;
(2)、若該運算符優先級小於或等於此運算符棧頂的運算符,則彈出棧頂運算符並輸出,重複比較、輸出,直到棧爲空或該運算符優先級高於棧頂運算符,而後將該運算符入棧。
五、重複上述操做(3)-(4)直至掃描完整個簡單算術表達式,肯定全部字符都獲得正確處理,輸出結果即是中綴表達式轉化爲逆波蘭表示的簡單算術表達式。 中綴表達式(a+b)*c-(a+b)/e的逆波蘭式是ab+c*ab+e/-。
逆波蘭表達式是一種利用棧來進行運算的數學表達式。
以一個表達式 1 + 2 * 3 爲例。
以順序方式輸入這個表達式,計算機首先須要解析這個表達式,而後遞歸的求值。
好比從左起進行順序解析,獲得一個符號樹:
+
/ \
1 *
/ \
2 3
計算機會遞歸的從葉子開始求值,最後回到根,得出結果。
因此對於一個比較複雜的表達式,樹可能會很深,並且遞歸的過程將會消耗大量的內存,因爲有解析和運算兩個過程,時間開銷也不理想。
若是將上式改成逆波蘭表達式: 3 2 * 1 +
那麼就能實現 「在線處理」。在線處理必須是這類問題在計算機中最快的算法。
仍是從左側開始,掃描這個表達式。
掃描到第一項,爲一個操做數,那麼能夠把這個數壓棧,而後繼續掃面。
第二項仍是一個操做數,一樣的壓棧繼續。
到第三項,獲得一個雙目運算符,這時候計算機就從操做數棧中彈出兩個數做爲運算符的兩個參數,而後進行運算,並將結果再壓回操做數棧。
接着第四項,一個操做數,壓棧。第五項,又是一個雙目運算符,那麼彈出兩個操做數進行運算,把結果壓棧。
到此,這個表達式就掃描結束了,操做數棧中最後會剩下表達式的運算結果。
不須要兩次操做,只要從頭掃描到尾就能求出結果,也不須要遞歸,只須要一個很小的棧就能夠。因此逆波蘭表達式算法取得了時間複雜度和空間複雜度上的雙重優點。
而且:
因此在機器上實現起來很是的方便。早期,處理器性能比較弱的時候,使用這種方式就能夠在性能不足的機器上實現任意複雜度的表達式運算。很棒吧。
至於你的問題,主要是在於「爲啥個人汽車不能飛」,很簡單,由於不支持。
使用一種軟件的時候須要按照軟件提供的功能來操做,編程語言也是一種軟件系統,若是你想在這裏面使用逆波蘭表達式,那麼須要軟件提供對逆波蘭表達式的支持。對於編程語言,就是語法級的支持。
編譯器在編譯時怎麼實現的我沒有研究過。可是對於常數值,在編譯時就能夠肯定,對於變量,我想仍是和平臺相關吧。對於寄存器比較少的平臺,有可能會把表達式的運算序列化成一個逆波蘭表達式。只是有可能,沒有研究過編譯器的實現。
應用主要是任何基於棧的程序語言,計算機(至關大一部分工程計算機都是基於棧的)。
Evaluate the value of an arithmetic expression in Reverse Polish Notation.
Valid operators are +, -, *, /. Each operand may be an integer or another expression.
Some examples:
["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6
計算逆波半表達式的值,有效的運算符是:+、-、*、/,每一個操做數要麼是一個整數要麼是另外一個表達式
使用棧進行操做
算法實現類
import java.util.Stack; public class Solution { public int evalRPN(String[] tokens) { // 參數校驗 if (tokens == null || tokens.length < 1) { throw new IllegalArgumentException(); } int op1; int op2; // 操做數棧 Stack<Integer> stack = new Stack<>(); for (String token: tokens) { // 說明是運算符,要取棧頂兩個元素進行運算 if ("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token)) { // 取棧頂元素 op2 = stack.pop(); op1 = stack.pop(); // 進行運算 switch (token.charAt(0)) { case '+': op1 += op2; break; case '-': op1 -= op2; break; case '*': op1 *= op2; break; case '/': op1 /= op2; break; } // 結果入棧 stack.push(op1); } // 說明是操做數,入棧 else { stack.push(Integer.parseInt(token)); } } return stack.pop(); } }
運算符優先級參考:
優先級分爲棧內優先級isp(In stack priority)和棧外優先級icp(In coming priority)。除了括號之外,其餘運算符進棧後優先級都升1,這樣能夠體如今中綴表達式中相同優先級的操做符自左向右計算的要求,讓位於棧頂的操做符先退棧並輸出。各運算符及符號優先級:
操做符 |
# |
^ |
*,/,% |
+,- |
( |
) |
isp |
0 |
7 |
5 |
3 |
1 |
8 |
icp |
0 |
6 |
4 |
2 |
8 |
1 |