中綴表達式轉後綴表達式並計算
中綴表達式就是我們正常工作中寫的表達式,如 a+(b-c)d ,編譯系統將中綴表達式改寫 abc-d+ ,這種運算符在操作數後面稱爲後綴表達式(也稱逆波蘭表達式)。
題目:輸入一箇中綴表達式,計算其結果。
輸入的前提假設:
(1)只考慮+、-、*、/這四種運算符,中綴表達式中只有一種括號:();
(2)輸入的中綴表達式中只有整數,沒有小數;
(3)假定輸入是合法的。
案例:1+ (2-3) * 4 + 6/3
提示:利用棧的“記憶”,符號都推入棧即可!
注意:計算一箇中綴表達式需要知道運算符的優先級和結合性。乘除是高優先級,加減是低優先級,優先級相同時他們都是左結合的,也就是從左計算到右。有括號就要計算括號內的表達式。
中綴表達式轉換爲後綴表達式
利用棧來實現轉換
轉換過程需要用到棧,這裏使用兩個棧:stack 棧用來存放運算符,post 棧用來存放最後的後綴表達式。具體規則如下:
從左到右掃描中綴表達式:
若是操作數,直接存入 post 棧;
若是運算符:
(1)該運算符是左括號 ( , 則直接存入 stack 棧。
(2)該運算符是右括號 ),則將 stack 棧中 ( 前的所有運算符出棧,存入 post 棧。
(3)若該運算符爲非括號,則將該運算符和 stack 棧頂運算符作比較:若高於棧頂運算符,則直接存入 stack 棧,否則將棧頂運算符出棧(從棧中彈出元素直到遇到發現更低優先級的元素(或者棧爲空)爲止),存入 post 棧。
(4)當掃描完後,stack 棧中還有運算符時,則將所有運算符出棧,存入 post 棧。
例子:中綴表達式 a + b * c + (d * e + f) * g,其轉換成後綴表達式則爲a b c * + d e * f + g * +。
具體過程如下:
掃描 | stack棧 | post棧 |
---|---|---|
a | 空 | a |
a+ | + | a |
a+b | + | ab |
a+b* | +* | ab |
a+b*c | +* | abc |
a+b*c+ | + | abc*+ |
a+b*c+( | +( | abc*+ |
a+b*c+(d | +( | abc*+d |
a+bc+(d | +(* | abc*+d |
a+bc+(de | +(* | abc*+de |
a+bc+(de+ | +(+ | abc*+de* |
a+bc+(de+f | +(+ | abc*+de*f |
a+bc+(de+f) | + | abc*+de*f+ |
a+bc+(de+f)* | +* | abc*+de*f+ |
a+bc+(de+f)*g | +* | abc*+de*f+g |
a+bc+(de+f)*g# | 空 | abc*+def+g+ |
注意:表格中第6步,讀到+,因爲棧頂元素的優先級高,所以出棧,棧中下一個元素+優先級與讀到的操作符+一樣,所以也要彈出。然後再將讀到的+壓入棧中。第13步,讀到),則直接將棧中元素彈出直到遇到(爲止。這裏左括號前只有一個操作符+被彈出。
因此,對於案例中的表達式,將其轉換爲後綴表達式後的結果爲123-4*+63/+。
另外,還有其他方法可以實現中綴表達式轉後綴表達式
利用語法樹
先將中綴表達式用二叉樹表示出來,再後序遍歷該二叉樹,就得到其相應的後綴表達式。
加括號法
加括號法先將中綴表達式每步要計算的表達式加上括號,然後將每個運算符移到其所在括號的外面,最後,從左到右去掉括號後就是後綴表達式。
*示例: a+(b-c)d
加括號後: (a+((b-c)d))
移運算符: (a((bc)-d)*)+
去括號 abc-d+
計算後綴表達式
後綴表達式的計算就相當簡單了。準備一個數字棧。從左到右掃描後綴表達式,如果是數字,放入數字棧。如果是符號,從數字棧中彈出兩個數字,第一個取出的數字爲右運算數,第二個爲左運算數,進行運算。然後將結果放進數字棧中。如此反覆,直到讀完整個表達式後,留在數字棧中的那個數字就是最終結果。
例如:假定待求值的後綴表達式爲:6 5 2 3 + 8 * + 3 + *,則其求值過程如下:
- 遍歷表達式,遇到數字首先放入棧,此時棧如下 6 5 2 3
- 接着讀到+,則彈出3和2,執行2+3,將結果5壓棧 6 5 5
- 讀到8,壓棧 6 5 5 8
- 讀到 , 彈出8和5,執行58,將結果40壓棧 6 5 40
- 讀到 +,彈出40和5,執行5+40,將結果45壓棧 6 45
- 讀到 3,壓棧 6 45 3
- 讀到 +,彈出3和45,執行45+3,將結果48壓棧 6 48
- 讀到 ,彈出48和6,執行648,將結果288壓棧 288
最後結果288
代碼如下:
package DataStructure;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
/**
* 中綴表達式轉後綴表達式並計算
*/
public class NifixExpression {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()) {
String exp = sc.nextLine();
// 將中綴表達式轉爲List形式
List<String> nifixExpression = nifix2List(exp);
System.out.println("NifixExpression = " + nifixExpression);
List<String> postfixExpression = nifix2postfix(nifixExpression);
System.out.println("postfixExpression = " + postfixExpression);
// 計算後綴表達式的值
int reslut = calculate(postfixExpression);
System.out.println(exp + " = " + reslut);
}
}
/**
* 根據後綴表達式計算結果
*
* @param postfixExpression
* @return
*/
private static int calculate(List<String> postfixExpression) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < postfixExpression.size(); i++) {
String item = postfixExpression.get(i);
if (isNumber(item)) {
//是數字
stack.push(Integer.parseInt(item));
} else {
//是操作符,取出棧頂兩個元素
int num2 = stack.pop();
int num1 = stack.pop();
int res = 0;
switch (item) {
case "+":
res = num1 + num2;
break;
case "-":
res = num1 - num2;
break;
case "*":
res = num1 * num2;
break;
case "/":
res = num1 / num2;
break;
default:
throw new RuntimeException("運算符錯誤!");
}
stack.push(res);
}
}
return stack.pop();
}
/**
* 將表達式轉爲List格式
*
* @param exp
* @return
*/
private static List<String> nifix2List(String exp) {
int index = 0;
List<String> list = new ArrayList<>();
do {
char chr = exp.charAt(index);
// 從48到57對應ASCII的0到9數字
if (chr <= 47 || chr >= 58) { //是操作符, 直接加入到list中
index++;
list.add(chr + "");
} else if (chr > 47 && chr < 58) { //是數字,需要判斷是否是多位
String str = "";
while (index < exp.length() && exp.charAt(index) > 47 && exp.charAt(index) < 58) {
str += exp.charAt(index);
index++;
}
list.add(str);
}
} while (index < exp.length());
return list;
}
private static List<String> nifix2postfix(List<String> exp) {
// 創建一個棧用來保存操作符
Stack<String> operatorStack = new Stack<>();
// 創建一個List用來保存後綴表達式
List<String> sufffixList = new ArrayList<>();
for (String item : exp) {
//得到數或操作符
if (isOperator(item)) {
//是操作符,判斷操作符棧是否爲空
if (operatorStack.isEmpty() || "(".equals(operatorStack.peek()) || priority(item) > priority(operatorStack.peek())) {
//爲空或者棧頂元素爲左括號或者當前操作符大於棧頂操作符的優先級則直接壓棧
operatorStack.push(item);
} else {
//否則將棧中元素出棧入隊,直到遇到大於當前操作符的優先級或者遇到左括號
while (!operatorStack.isEmpty() && !"(".equals(operatorStack.peek())) {
if (priority(item) <= priority(operatorStack.peek())) {
sufffixList.add(operatorStack.pop());
}
}
// 當前操作符壓棧
operatorStack.push(item);
}
} else if ((isNumber(item))) {
//是數字則直接入隊
sufffixList.add(item);
} else if ("(".equals(item)) {
//是左括號,直接壓棧
operatorStack.push(item);
} else if (")".equals(item)) {
//是右括號,則將棧中元素彈出,直到遇到左括號,左括號出棧,但不入隊
while (!operatorStack.isEmpty()) {
if ("(".equals(operatorStack.peek())) {
operatorStack.pop();
break;
} else {
sufffixList.add(operatorStack.pop());
}
}
} else {
throw new RuntimeException("有非法字符!");
}
}
//循環完畢,如果操作符棧中元素不爲空,將棧中元素出棧入隊
while (!operatorStack.isEmpty()) {
sufffixList.add(operatorStack.pop());
}
return sufffixList;
}
/**
* 判斷是否爲數字
*
* @param item
* @return
*/
private static boolean isNumber(String item) {
return item.matches("\\d+");
}
/**
* 獲取操作符的運算優先級
*
* @param item
* @return
*/
private static int priority(String item) {
if (item.equals("*") || item.equals("/")) {
return 1;
} else if (item.equals("+") || item.equals("-")) {
return 0;
}
return -1;
}
/**
* 判斷是否爲操作符
*
* @param item
* @return
*/
private static boolean isOperator(String item) {
return item.equals("+") || item.equals("-") || item.equals("*") || item.equals("/");
}
}