1.前綴表達式
前綴表達式是一種沒有括號的算術表達式,與中綴表達式不同的是,其將運算符寫在前面,操作數寫在后面。為紀念其發明者波蘭數學家Jan Lukasiewicz,前綴表達式也稱為“波蘭式”。例如,- 1 + 2 3,它等價於1-(2+3)。
例如,(a+b)*(c+d)轉換為*,+,a,b,+,c,d。
后面的前綴表達式的運算方式為:如果當前字符(或字符串)為數字或變量,則壓入棧內;如果是
運算符,則將棧頂兩個元素彈出棧外並作相應運算,再將結果壓入棧內。當前綴表達式掃描結束時,棧里的就是中綴表達式運算的最終結果。對比中綴運算的步驟,不難發現前綴運算在計算機上的優勢。
對
前綴表達式求值,要從右至左掃描表達式,首先從右邊第一個字符開始判斷,若當前字符是數字則一直到數字串的末尾再記錄下來,若為
運算符,則將右邊離得最近的兩個“數字串”作相應
運算,然后以此作為一個新的“數字串”並記錄下來;掃描到表達式最左端時掃描結束,最后運算的值即為
表達式的值。
例如:對
前綴表達式“- 1 + 2 3”求值,掃描到3時,記錄下這個數字串,掃描到2時,記錄下這個數字串,當掃描到+時,將+右移做相鄰兩數字串的運算符,記為2+3,結果為5,記錄下5這個新數字串,然后繼續向左掃描,掃描到1時,記錄下這個數字串,掃描到-時,將-右移做相鄰兩數字串的運算符,記為1-5,結果為-4,此時關於這個表達式的全部運算已完成,故表達式的值為-4。
中綴表達式轉化為前綴表達式的例子:
a+b ---> +,a,b
a+(b-c) ---> +,a,-,b,c
a+(b-c)*d ---> +,a,*,-,b,c,d
a+1+3 ---> +,+,a,1,3
(1) 首先構造一個
運算符棧(也可放置括號),運算符(以括號為分界點)在棧內遵循越往棧頂優先級不降低的原則進行排列。
(2)從右至左掃描
中綴表達式,從右邊第一個字符開始判斷:
如果當前
字符是數字,則分析到數字串的結尾並將數字串直接輸出。
如果是運算符,則比較
優先級。如果當前運算符的優先級大於等於棧頂運算符的優先級(當棧頂是括號時,直接入棧),則將
運算符直接入棧;否則將棧頂運算符
出棧並輸出,直到當前運算符的優先級大於等於棧頂運算符的優先級(當棧頂是括號時,直接入棧),再將當前運算符入棧。
如果是括號,則根據括號的方向進行處理。如果是向右的括號,則直接入棧;否則,遇向左的括號前將所有的運算符全部出棧並輸出,遇右括號后將向左、向右的兩括號一起出棧(並不輸出)。
(3) 重復上述操作(2)直至掃描結束,將棧內剩余運算符全部出棧並輸出,再逆綴輸出字符串。
中綴表達式也就轉換為前綴表達式了。

將
中綴表達式“1+((2+3)*4)-5”轉換為前綴表達式。
中綴表達式
|
前綴表達式
|
(棧尾)運算符棧(棧頂)
|
說明
|
5
|
5
|
空
|
5,是數字串直接輸出
|
-
|
5
|
-
|
-,棧內無運算符,直接入棧
|
)
|
5
|
-)
|
),直接入棧
|
4
|
5 4
|
-)
|
4,是數字串直接輸出
|
*
|
5 4
|
-)*
|
*,棧頂是括號,直接入棧
|
)
|
5 4
|
- ) * )
|
),直接入棧
|
3
|
5 4 3
|
- ) * )
|
3,是數字串直接輸出
|
+
|
5 4 3
|
- ) * ) +
|
+,棧頂是括號,直接入棧
|
2
|
5 4 3 2
|
- ) * )+
|
2,是數字串直接輸出
|
(
|
5 4 3 2+
|
- ) *
|
(,與棧里最后一個)抵消,並釋放它們之間的+
|
(
|
5 4 3 2+*
|
-
|
(,方法與上類同,請參考下一目錄
|
+
|
5 4 3 2+*
|
-+
|
+,優先級
大於等於棧頂運算符,直接入棧
|
1
|
5 4 3 2+*1
|
-+
|
1,是數字串直接輸出
|
空
|
5 4 3 2+*1+-
|
空
|
掃描結束,將棧內剩余運算符全部出棧並輸出
|
空
|
- + 1 * + 2 3 4 5
|
空
|
逆綴輸出字符串
|
對運算符的具體處理方法如下:
):直接入棧
(:遇)前,將運算符全部出棧並輸出;遇)后,將兩括號一起刪除①
+、-:1
*、/、%:2
^:3
2.后綴表達式
一個
表達式E的后綴形式可以如下定義:
(2)如果E是E1 op E2形式的表達式,這里op是任何二元操作符,則E的后綴式為E1'E2' op,這里E1'和E2'分別為E1和E2的后綴式。
(3)如果E是(E1)形式的表達式,則E1的后綴式就是E的后綴式。
(a+b)*c-(a+b)/e的后綴表達式為:
(a+b)*c-(a+b)/e
→((a+b)*c)((a+b)/e)-
→((a+b)c*)((a+b)e/)-
→(ab+c*)(ab+e/)-
→ab+c*ab+e/-

將一個普通的中綴表達式轉換為
逆波蘭表達式的一般算法是:
首先需要分配2個棧,一個作為臨時存儲運算符的棧S1(含一個結束符號),一個作為輸入逆波蘭式的棧S2(空棧),S1棧可先放入優先級最低的運算符#,注意,中綴式應以此最低優先級的運算符結束。可指定其他字符,不一定非#不可。從中綴式的左端開始取字符,逐序進行如下步驟:
(1)若取出的字符是
操作數,則分析出完整的運算數,該操作數直接送入S2棧
(2)若取出的字符是
運算符,則將該運算符與S1棧棧頂元素比較,如果該
運算符優先級(不包括括號運算符)大於S1棧棧頂運算符優先級,則將該運算符進S1棧,否則,將S1棧的棧頂運算符彈出,送入S2棧中,直至S1棧棧頂運算符低於(不包括等於)該運算符優先級,最后將該運算符送入S1棧。
(3)若取出的字符是“(”,則直接送入S1棧頂。
(4)若取出的字符是“)”,則將距離S1棧棧頂最近的“(”之間的運算符,逐個
出棧,依次送入S2棧,此時拋棄“(”。
(5)重復上面的1~4步,直至處理完所有的輸入字符
(6)若取出的字符是“#”,則將S1棧內所有運算符(不包括“#”),逐個出棧,依次送入S2棧。
完成以上步驟,S2棧便為逆波蘭式輸出結果。不過S2應做一下逆序處理。便可以按照逆波蘭式的計算方法計算了!

代碼實現
public class PolandNotation { public static void main(String[] args) { //創建逆波蘭表達式 //(3+4)*5-6 => 3 4 + 5 * 6 - /* String suffixExpression= "3 4 + 5 * 6 -"; *//*1.將3 4 + 5 * 6 - =》放到List中 * 2.將list利用方法遍歷list配合棧進行計算*//* List<String> rpnlist = getList(suffixExpression); System.out.println("rpnlist=" +rpnlist); int res = calculation(rpnlist); System.out.printf("計算結果: %d\n",res);*/ /*將中綴表達式轉換成后綴表達式*/ //1+((2+3)*4)-5 ====>1 2 3 + 4 * + 5 - /*將字符串轉換成中綴的list*/ String expression = "1+((2+3)*4)-5"; List<String> infixExpressionList = toInfixExpressionList(expression); System.out.println("后綴表達式對應的List=" + infixExpressionList); List<String> parseSuffixExpressionList = parseSuffixExpressionList(infixExpressionList); //結果List=[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] System.out.println("后綴表達式對應的List= " + parseSuffixExpressionList); //結果 List= [1, 2, 3, +, 4, *, +, 5, -] int calculation = calculation(parseSuffixExpressionList); System.out.printf("計算結果為:%d\n",calculation);//=16 } /*將字符串轉換成中綴的list*/ public static List<String> toInfixExpressionList(String s){ List<String> ls= new ArrayList<>(); int i=0;//指針:遍歷中綴字符串 String str;//多位數拼接 char c;//保存字符 放入c; do { //c是數字:進行字符串拼接 if ((c=s.charAt(i))<48 || (c=s.charAt(i))>57){ ls.add(""+c); i++; }else {//考慮多位數問題 str="";// while (i<s.length()&&(c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){ str+=c; i++; } ls.add(str); } }while (i<s.length()); return ls; } /*中綴表達式轉成后綴表達式*/ public static List<String> parseSuffixExpressionList(List<String> ls){ Stack<String> s1 = new Stack<>(); //符號棧 //第二個棧不需要pop操作,而且需要逆向輸出所以利用list代替 List<String> s2 = new ArrayList<>(); for (String item : ls) { /*如果是數入s1*/ if (item.matches("\\d+")){ s2.add(item); }else if (item.equals("(")){ s1.push(item); }else if (item.equals(")")){ /*依次彈出s1棧頂的運算符,並壓入s2,知道遇到左括號為止,此時將左括號丟棄*/ while (!s1.peek().equals("(")){ s2.add(s1.pop()); } s1.pop();//將左括號丟棄消除小括號 }else { /*當item的優先級小於等於s1棧頂的運算符的優先級 * 1.將s1的運算符彈出並加入到s2中,依次比較新的運算符 * 方法比較運算符的優先級的方法*/ while (s1.size()!=0 && Operation.getValue(s1.peek())>=Operation.getValue(item)){ s2.add(s1.pop()); } //將item壓入棧中 s1.push(item); } } /*將s1剩余的運算符壓入棧中*/ while (s1.size()!=0){ s2.add(s1.pop()); } return s2;//存放到list,因此按順序輸出就是逆波蘭表達式 } /*將逆波蘭表達式放入到list*/ public static List<String> getList(String suffixExpression){ //字符串分割 String[] split = suffixExpression.split(" "); List<String> list = new ArrayList<>(); for (String ele : split) { list.add(ele); } return list; } /*完成逆波蘭運算*/ public static int calculation(List<String> ls){ //創建一個棧 Stack<String> stack = new Stack<String>(); for (String item : ls) { /*使用正則表達式*/ if (item.matches("\\d+")){ stack.push(item);//入棧 }else { int num2 =Integer.parseInt(stack.pop());//取出pop2個數據進行運算再入棧 int num1 =Integer.parseInt(stack.pop()); int res = 0; if (item.equals("+")){ res = num1+num2; }else if (item.equals("-")){ res = num1-num2; }else if (item.equals("*")){ res= num1*num2; }else if (item.equals("/")){ res = num1/num2; }else { throw new RuntimeException("運算符號錯誤"); } //將結果入棧 stack.push(res+""); } } //留着棧中的就是計算結果 return Integer.parseInt(stack.pop()); } }