中綴表達式,就是在表達式中,操作符在操作數的中間,比如 (1+2)*3,+和*在1, 2, 3的中間。前綴表達式,就是操作符在操作數的前面,比如 +12,+在1, 2的前面。后綴表達式,就是操作符在操作數的后面,比如 12+,+在1, 2的后面。為什么會有這么多表達式呢?它們目的不同。中綴表達式,便於我們書寫,也符合我們的閱讀習慣,在計算機程序中,都是寫中綴表達式,但它卻不利於計算機進行算術計算,因為涉及到優先級和括號。前綴表達式和后綴表達式中沒有括號,並且運算符的優先級也通過它們在表達式中的順序體現出來了,有利於計算機進行算術運算。前綴表達式也叫波蘭表達式,因為它是由一個波蘭人發明的,相應的,后綴表達式也稱為逆波蘭表達式。舉個例子來說明一下三者的運算過程,假設計算(3+4)*5-6
使用中綴表達式進行計算
1,創建兩個棧,一個是數字棧,用來存放操作數,一個是字符棧,用來存放操作符。
2,算術表達式是一個字符串,從左到右循環遍歷表達式,依次取出每一個字符。
當取出的字符是操作數時,入數字棧。
當取出的字符是操作符時,這時還要看操作符的優先級和字符棧是否為空
如果字符棧為空,直接把取出的字符放入到字符棧中。
如果字符棧不為空,則要比較字符的優先級
如果取出的字符的優先級大於等於字符棧中棧頂的字符的優先級,直接把取出的字符放入到字符棧中。
如果取出的字符的優先級比字符棧中棧頂的字符的優先級低,則要循環進行如下操作,直到取出的字符的優先級大於等於字符棧中棧頂字符的優先級或者棧為空,此時,把取出的字符放入到字
符棧中。如下操作就是:
1,從字符棧中彈出操作符
2,從數字棧中彈出兩個操作數。
3,操作數結合操作符進行計算,要注意操作數順序,后面pop出的數在求值的表達式中是前面的操作數,尤其是在做減法的時候
4,計算出的值放入到數字棧。
為什么要比較操作符的優先級呢?因為要確定操作數屬於哪個操作符,相鄰兩個操作符之間的數字是共享的。2+3*5. 3屬於+,也屬於*
當取出的字符是( 時,直接入字符棧,因為要等到), 左括號本身不能決定計算的順序
當取出的字符是)時,那就要循環執行如下操作,直到碰到(。此后把(彈出來,舍棄掉
1,從字符棧中彈出操作符
2,從數字棧中彈出兩個操作數。
3,操作數結合操作符進行計算,要注意操作數順序,后面pop出的數在求值的表達式中是前面的操作數,尤其是在做減法的時候
4,計算出的值放入到數字棧。
3,表達式循環完畢后,如果字符棧不為空,還是執行如下操作,直到棧為空
1,從字符棧中彈出操作符
2,從數字棧中彈出兩個操作數。
3,操作數結合操作符進行計算,要注意操作數順序,后面pop出的數在求值的表達式中是前面的操作數,尤其是在做減法的時候
4,計算出的值放入到數字棧。
4,字符棧為空,數字棧中只剩下一個數字,這個數字就是表達式的值。
看一下具體的實現過程,兩個棧number, operator,Java中有Stack類。怎么判斷字符是數字,Java中有 Character.isDigit() ,如果沒有這個函數也沒有關系,只要一個字符>'0' 且小於‘9’ 就可以
private static boolean isDigit(char c){ return c >='0' && c <= '9'; }
怎么判斷是操作符呢,假設只計算加減乘除,只要字符是‘+’或‘-’ 或‘*'或/ 就可以
private static boolean isOperator(char value) { return value == '*' || value == '/' || value == '+' || value == '-'; }
優先級呢?用數字表示吧,數字越大,優先級越高
// 數字越大,優先級越高。 private static int priority(char operator) { if(operator == '*' || operator == '/' ) { return 1; } else if (operator == '+' || operator == '-' ) { return 0; } else { return -1; } }
最后定義一下操作,假設數字棧就是number,字符棧就是operator,操作就是
private static void performOperation(Stack<Integer> numbers, Stack<Character> operators) { int num1 = numbers.pop(); int num2 = numbers.pop(); char operator = operators.pop(); int result = 0; switch (operator) { case '+': { result = num2 + num1; break; } case '-': { result = num2 - num1; break; } case '*': { result = num2 * num1; break; } case '/': { result = num2 / num1; break; } } numbers.push(result); } }
那整個表達式的運算過程就是
public static int evaluate(String expression){ // 創建數字棧和字符棧 Stack<Integer> numbers = new Stack<>(); Stack<Character> operators = new Stack<>(); // 循環遍歷表達式 for (int i = 0; i < expression.length(); i++) { // 取出每一個字符 char c = expression.charAt(i); // 如果取出的字符是操作數, 轉化成數字,入數字棧 if (Character.isDigit(c)){ numbers.push(Integer.parseInt(String.valueOf(c))); } // 取出的字符是操作符 else if(isOperator(c)) { if (operators.isEmpty()){ // 如果棧為空 operators.push(c); } else if (priority(c) >= priority(operators.peek())){ // 優先級高 operators.push(c); } else { while (!operators.isEmpty() && priority(c) < priority(operators.peek())){ // 優先級低 performOperation(numbers, operators); } operators.push(c); } } // 左括號直接入棧 else if (c== '('){ operators.push(c); } // 右括號時,依次彈棧,直到遇到左括號 else if(c == ')'){ while (operators.peek() != '('){ performOperation(numbers, operators); } // 舍棄左括號 operators.pop(); } else { // 其他符號,什么都不做,也可以continue continue; } } // 循環完畢后,如果字符棧不為空 while (!operators.isEmpty()){ performOperation(numbers, operators); } return numbers.pop(); }
使用前綴表達式進行計算
(3+4)*5-6的前綴表達式是-*+3456,計算規則如下
1,創建一個數字棧,用於存放數字
2,從右向左掃描,遇到數字,將數字壓入到棧中,遇到操作符時,彈出棧頂的兩個數,用操作符對它們做相應的計算,並將結果入棧
3,重算上述過程直到表達式的最左端,最后運算出的值即為表達式的結果
從右向左掃描,將6 5 4 3 壓入棧中,遇到+, 彈出3和4,並計算3+4,得到7,將7入棧,再運到*, 彈出7和5,計算7*5 =35,將35入棧,最后-, 將35和6彈出,35-6 = 29,已經掃描到最左邊了,所以29就是最后的值。要注意操作數順序,pop出的數和它在求值的表達式中的順序是一致的。
public static int prefixEvaluation(String expression) { // 創建數字棧 Stack<Integer> numbers = new Stack<>(); // 循環遍歷表達式 for (int i = expression.length() - 1; i >= 0; i--) { // 取出每一個字符 char c = expression.charAt(i); // 如果取出的字符是操作數, 轉化成數字,入數字棧 if (Character.isDigit(c)){ numbers.push(Integer.parseInt(String.valueOf(c))); } else { // 取出的字符是操作符,那就pop 出兩個數,進行計算,再入棧 int num1 = numbers.pop(); int num2 = numbers.pop(); int result = 0; // 操作符,加減,乘除 if(c == '+') { result = num1 + num2; } else if (c == '-') { result = num1 - num2; } else if (c == '*') { result = num1 * num2; } else { result = num1 / num2; } numbers.push(result); } } return numbers.pop(); }
使用后綴表達式進行計算
(3+4)*5-6 對應的后綴表達式 3 4 + 5 * 6 -
后綴表達式的算法和前綴表達式一樣,只不過是從左向右遍歷表達式
1,創建一個數字棧,用於存放數字
2,從左向右掃描表達式,遇到數字,將數字壓入到棧中,遇到操作符時,彈出棧頂的兩個數,用操作符對它們做相應的計算,並將結果入棧
3,重算上述過程直到表達式的最右端,最后運算出的值即為表達式的結果
3,4入棧,遇到+號,3,4出棧,進行相加,得到7,將7入棧,遇到5,入棧,遇到*,彈出7和5,相乘,得到35,入棧遇到6,將其入棧,遇到-號,彈出35和6,進行相減,得出29。由於到了表達式的最右邊,29就是結果了。
public static int suffixEvaluation(String expression) { // 創建數字棧 Stack<Integer> numbers = new Stack<>(); // 循環遍歷表達式 for (int i = 0; i < expression.length(); i++) { // 取出每一個字符 char c = expression.charAt(i); // 如果取出的字符是操作數, 轉化成數字,入數字棧 if (Character.isDigit(c)){ numbers.push(Integer.parseInt(String.valueOf(c))); } else { // 取出的字符是操作符,那就pop 出兩個數,進行計算,再入棧 int num1 = numbers.pop(); int num2 = numbers.pop(); int result = 0; // 操作符,加減,乘除 if(c == '+') { result = num2 + num1; } else if (c == '-') { result = num2 - num1; } else if (c == '*') { result = num2 * num1; } else if(c == '/') { result = num2 / num1; } numbers.push(result); } } return numbers.pop(); }