前缀、中缀和后缀表达式详解,中缀表达式到后缀表达式的转换规则,以及后缀表达式的计算规则,附计算代码
1. 中綴、前綴和后綴表達式
1.1 中綴表達式
首先,中綴表達式的這個“綴”指運算符在兩個操作數的位置。中綴表達式其實就是我們常用的算術表達式,比如 2 + 9 - (32?* (19 - 4) +14),我們很容易就可以得到計算結果,但是對于計算機來說,它們就得對各個運算符進行優先級比較以及彈棧和入棧等操作,其實計算機對于前綴表達式和后綴表達式更容易理解。
1.2 前綴表達式
前綴表達式,也稱波蘭式,指運算符處于兩個操作數的前面,比如 2 + 3,那么前綴表達式就是 + 2 3;對于復雜點的表達式,如 13 * ((3 + 8) * 4),前綴表達式為 * 13 * + 3 8 4,后續分析怎么轉換。
1.3?后綴表達式
也稱逆波蘭式,指運算符處于兩操作數后面,比如 2 + 3,后綴表達式為 2 3 +;對于復雜點的表達式,如?13 * ((3 + 8) * 4),后綴表達式為?13 3 8 + 4 * *,后續會講怎么轉換。
?
2. 中綴表達式到后綴表達式的轉換規則
前面我們提到計算機易于理解前綴表達式和后綴表達式,但我們生活中或使用計算器時是中綴表達式,這也就意味著我們需要將中綴表達式轉換為前綴或后綴表達式,從而實現計算器的高效計算。
中綴表達式轉換為后綴表達式的規則如下:
1.創建運算符棧s1和操作數數組a2,然后掃描中綴表達式;2.如果是操作數,直接放入數組a2;3.如果是運算符,棧s1為空或棧頂符號為左括號,或者優先級比棧頂運算符高,則入棧結束該步驟;否則將s1棧頂運算符彈出放入操作數數組a2,然后重復該步驟3。4.如果是左括號,直接壓入運算符棧s1;如果是右括號,依次彈出s1的運算符放入s2,直至遇到左括號結束,并將左、右括號舍棄。5.循環步驟2-4直至表達式掃描結束,將s1的剩余運算符依次彈出放入數組a2,數組a2就是后綴表達式。以下演示一個較復雜中綴表達式 3 * (11 - 8) - 45 / ((98 - 60) / 10 + 6) ?轉換后綴表達式的流程,流程如下。
1. 先創建運算符棧s1,和操作數數組a2,然后索引指向中綴表達式的第一位;
2. 3是操作數,放入數組a2的第一個位置,得到a2 = {3};
3. *是運算符,因為棧s1為空,*直接入棧s1,s1 = ;
4. 遇到左括號,直接壓入運算符棧s1,s1 =?;
5. 11是操作數,直接放入數組a2,得到a2 = {3, 11};
6. - 是運算符,因為棧頂符號是左括號,- 直接入棧得到s1 =?
7.?8是操作數,直接放入數組a2,得到a2 = {3, 11, 8};
8. 遇到右括號,彈出s1中的運算符 - ,直到遇到左括號結束;此時a2={3, 11, 8, -},s1?=?
9.?- 是運算符,因為優先級低于棧頂符號 * ,將s1棧頂符號彈出放入數組a2,a2變為{3, 11, 8, -, *},s1?=?
10. 重復步驟3,由于此時棧為空,則 - 直接入棧,s1?=?
11. 45是操作數,直接放入數組a2,得到a2 = {3, 11, 8, -, *, 45};
12. / 是運算符,而且運算優先級高于s1的棧頂符號 -,所以直接入棧,s1 =?
13.?遇到左括號,直接壓入運算符棧s1,s1 =?;
14.?遇到左括號,直接壓入運算符棧s1,s1 =?;
15. 98是操作數,直接放入數組a2,得到a2 = {3, 11, 8, -, *, 45, 98};
16.?- 是運算符,因為棧頂符號是左括號,- 直接入棧,得到s1 =?
17. 60是操作數,直接放入數組a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60};
18.?遇到右括號,彈出s1中的運算符 - ,直到遇到左括號結束;此時a2={3, 11, 8, -, *, 45, 98, 60, -},s1?=?
19.?/ 是運算符,而且棧頂符號是左括號,/?直接入棧,得到s1 =?
20. 10是操作數,直接放入數組a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60, -, 10};
21. +?是運算符,因為優先級低于棧頂符號 /?,將s1棧頂符號 / 彈出放入數組a2,a2變為{3, 11, 8, -, *, 45, 98, 60, -, 10, /},
? ? ? s1 =?
22. + 運算符接著和棧頂符號比較,此時由于棧頂符號是左括號,直接入棧,s1 =?
23. 6是操作數,直接放入數組a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60, -, 10, /, 6};
24.?遇到右括號,彈出s1中的運算符 +?,直到遇到左括號結束;此時a2={3, 11, 8, -, *, 45, 98, 60, -, 10, /, 6, +},
? ? ?s1 =?
25. 掃描結束,彈出s1中剩余運算符至數組a2,得到a2 = {3, 11, 8, -, *, 45, 98, 60, -, 10, /, 6, +, /, -}
因此,最終得到的后綴表達式為?3?11?8?-?*?45?98?60?-?10?/?6?+?/?-
?
代碼如下
static int priority(char c1){if((c1 == '*') || (c1 == '/')){return 2;}else{return 1;} }static List infixToPostfix(String infixExpress){char c;Stack s1 = new Stack();List<String> a2 = new ArrayList();for (int i = 0; i < infixExpress.length(); i++) {c = infixExpress.charAt(i);if((c == '*') || (c == '/') || (c == '+') || (c == '-')){//如果棧空、棧頂元素是左括號,或者運算符優先級高于棧頂元素優先級,則入棧if((s1.isEmpty()) || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement()))){s1.push(c);}else{a2.add(String.valueOf(s1.pop()));//循環比較,直至運算符可以入棧,結束;否則一直將棧頂元素彈出放入數組a2while(!(s1.isEmpty() || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement())))){a2.add(String.valueOf(s1.pop()));}s1.push(c);}}else if(c == '('){s1.push(c);}else if(c == ')'){char c2 = (char)s1.pop();//循環比較,直至匹配到左括號結束;否則一直將棧頂元素彈出放入數組a2while(c2 != '('){a2.add(String.valueOf(c2));c2 = (char)s1.pop();}}else if((c >= 48) && (c <= 57)){//如果是最后一位,那么直接將數字放入數組a2,結束if(i == infixExpress.length()-1){a2.add(String.valueOf(c));}else{String s2 = "" + c;int cnt = 1;char c3 = infixExpress.charAt(i+cnt);while((c3 >= 48) && (c3 <= 57)){s2 += c3;cnt++;//一直掃描數字,直至中綴表達式末尾,結束if(i+cnt < infixExpress.length()){c3 = infixExpress.charAt(i+cnt);}else{break;}}i += (cnt-1);a2.add(s2);}}else{//如果字符是空格或者表達式最后一個字符是 = ,不操作,否則表達式異常if(((i == infixExpress.length()-1) && (c == '=')) || (c == ' ')){}else{System.out.println("Express is fault");}}}//最后需要將s1剩余的運算符彈出,放入數組a2中。while(! s1.isEmpty()){a2.add(String.valueOf(s1.pop()));}return a2;}?
3. 后綴表達式的計算原則
我們得到后綴表達式?3?11?8?-?*?45?98?60?-?10?/?6?+?/?-,那么怎么計算呢?其實,后綴表達式計算原則很簡單,主要分為三步,如下
1. 創建一個棧,并且從左至右掃描表達式;
2. 遇到數字,將數字壓入棧中;遇到運算符則彈出棧頂的兩個元素,使用運算符進行計算,然后將計算結果再壓入棧中;
3.?重復步驟2直到表達式掃描結束,最后彈出棧頂元素就是計算結果。
?
以上示例后綴表達式的計算步驟如下:
1. 創建棧stack,掃描表達式,將數字3, 11, 8依次壓入棧stack,得到stack =?
2. 遇到運算符 - ,棧stack彈出 8 和 11,計算 11 - 8 得到3,入棧得到stack =?
3.?遇到運算符 *?,棧stack彈出 3?和 3,計算得到 9,入棧得到stack =?
4. 將數字45, 98, 60壓入棧,得到stack =?
5.?遇到運算符 - ,棧stack彈出 60?和 98,計算 98- 60得到38,入棧得到stack =?
6.?將數字10壓入棧,得到stack =?
7.?遇到運算符 /?,棧stack彈出 10?和 38,計算 38 /?10得到3 (java整數相除得到整數3,其它變成語言可得到3.8),入棧得到
? ? stack =?
8.?將數字6壓入棧,得到stack =?
9. 遇到運算符 +?,棧stack彈出 6?和 3,計算 3 + 6 得到9,入棧得到stack =?
10.?遇到運算符 /?,棧stack彈出 9?和 45,計算 45?/?9?得到5,入棧得到stack =?
11.?遇到運算符 -?,棧stack彈出 5?和 9,計算 9?-?5?得到4,入棧得到stack =?
12. 計算結束,彈出棧頂元素 4,所以計算結果為4.
?
代碼如下
static String calculate(List<String> ls){Stack<String> st_result = new Stack();for (String s:ls) {// 因為這個計算后綴表達式的方法是和中綴表達式轉后綴表達式聯合使用的,而且中綴表達式// 轉換后綴表達式的方法中考慮了運算符異常等異常,所以以下代碼不考慮異常情況if(("-".equals(s)) || ("+".equals(s)) || ("*".equals(s)) || ("/".equals(s))){int num_back = Integer.valueOf(st_result.pop());int num_front = Integer.valueOf(st_result.pop());switch(s){case "-":st_result.push((num_front - num_back) + "");break;case "/":st_result.push((num_front / num_back) + "");break;case "+":st_result.push((num_front + num_back) + "");break;case "*":st_result.push((num_front * num_back) + "");break;}}else {st_result.push(s);}}return st_result.pop();}?
?
完整測試代碼以運行結果如下
static int priority(char c1){if((c1 == '*') || (c1 == '/')){return 2;}else{return 1;}}static List infixToPostfix(String infixExpress){char c;Stack s1 = new Stack();List<String> a2 = new ArrayList();for (int i = 0; i < infixExpress.length(); i++) {c = infixExpress.charAt(i);if((c == '*') || (c == '/') || (c == '+') || (c == '-')){//如果棧空、棧頂元素是左括號,或者運算符優先級高于棧頂元素優先級,則入棧if((s1.isEmpty()) || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement()))){s1.push(c);}else{a2.add(String.valueOf(s1.pop()));//循環比較,直至運算符可以入棧,結束;否則一直將棧頂元素彈出放入數組a2while(!(s1.isEmpty() || ((char)s1.lastElement() == '(') || (priority(c) > priority((char)s1.lastElement())))){a2.add(String.valueOf(s1.pop()));}s1.push(c);}}else if(c == '('){s1.push(c);}else if(c == ')'){char c2 = (char)s1.pop();//循環比較,直至匹配到左括號結束;否則一直將棧頂元素彈出放入數組a2while(c2 != '('){a2.add(String.valueOf(c2));c2 = (char)s1.pop();}}else if((c >= 48) && (c <= 57)){//如果是最后一位,那么直接將數字放入數組a2,結束if(i == infixExpress.length()-1){a2.add(String.valueOf(c));}else{String s2 = "" + c;int cnt = 1;char c3 = infixExpress.charAt(i+cnt);while((c3 >= 48) && (c3 <= 57)){s2 += c3;cnt++;//一直掃描數字,直至中綴表達式末尾,結束if(i+cnt < infixExpress.length()){c3 = infixExpress.charAt(i+cnt);}else{break;}}i += (cnt-1);a2.add(s2);}}else{//如果字符是空格或者表達式最后一個字符是 = ,不操作,否則表達式異常if(((i == infixExpress.length()-1) && (c == '=')) || (c == ' ')){}else{System.out.println("Express is fault");}}}//最后需要將s1剩余的運算符彈出,放入數組a2中。while(! s1.isEmpty()){a2.add(String.valueOf(s1.pop()));}return a2;}static String calculate(List<String> ls){Stack<String> st_result = new Stack();for (String s:ls) {// 因為這個計算后綴表達式的方法是和中綴表達式轉后綴表達式聯合使用的,而且中綴表達式// 轉換后綴表達式的方法中考慮了運算符異常等異常,所以以下代碼不考慮異常情況if(("-".equals(s)) || ("+".equals(s)) || ("*".equals(s)) || ("/".equals(s))){int num_back = Integer.valueOf(st_result.pop());int num_front = Integer.valueOf(st_result.pop());switch(s){case "-":st_result.push((num_front - num_back) + "");break;case "/":st_result.push((num_front / num_back) + "");break;case "+":st_result.push((num_front + num_back) + "");break;case "*":st_result.push((num_front * num_back) + "");break;}}else {st_result.push(s);}}return st_result.pop();}public static void main(String[] args) {String s = "3 * (11 - 8) - 45 / ((98 - 60) / 10 + 6)";List posefixList = infixToPostfix(s);System.out.print("后綴表達式為:");for (Object o:posefixList) {System.out.print(o + " ");}System.out.println("\n后綴表達式計算結果是 " + calculate(posefixList));}?
?
總結
以上是生活随笔為你收集整理的前缀、中缀和后缀表达式详解,中缀表达式到后缀表达式的转换规则,以及后缀表达式的计算规则,附计算代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 修饰符private和protected
- 下一篇: 手把手详解堆排序,堆就这么难懂?没有人看