棧的應用-四則運算表示式求值
Java實現四則運算表示式求值
前言
最近在複習資料結構與演算法,在棧的應用中瞭解到計算機計算四則運算表示式的演算法。
計算機計算四則運算主要分兩步:
- 將中綴表示式轉化為字尾表示式;
- 將字尾表示式進行運算得出結果。
字尾(逆波蘭)表示式
字尾表示式是一種不包含括號,運算子放在兩個運算物件的後面的表示法,比如四則運算表示式9+(3-1)*3+10/2,其後綴表示式為9 3 1 - 3 * + 10 2 / +。這是計算機採用的形式,計算過程如下:
9 3 1 - 3 * + 10 2 / +
9 2 3 * + 10 2 / +
9 6 + 10 2 / +
15 10 2 / +
15 5 +
20
方法就是:從左到右,將運算子前面的兩個數,分別做為運算數和被運算數,如:3 1 - 就等於(3-1)=2,依次進行計算。
中綴轉字尾
怎樣將中綴表示式轉成字尾表示式?
中綴表示式 “9+(3-1)*3+10/2” 轉化為字尾表示式 “9 3 1 - 3 * + 10 2 / +”
數字輸出,運算子進棧,括號匹配出棧,棧頂優先順序不低出棧
規則:從左到右遍歷中綴表示式的每個數字和符號,若是數字就輸出;是開括號‘(’,直接入棧;若是閉括號‘)’,則依次出棧,匹配前一個開括號;若棧頂運算子的優先順序大於或等於當前運算子,棧頂出棧。
具體過程:
第一個字元9是數字,輸出“9”,後面是符號“+”,入棧;
後面是開括號“(”,直接入棧;
然後是數字3,輸出,表示式變為“9 3”,接著是“-”入棧,然後是數字1,輸出,表示式為“9 3 1”;
接下來是閉括號“)”,需要去匹配前面的“(”,棧頂依次出棧,並輸出。因此”-“出棧並輸出,表示式為“9 3 1 -”,“(”出棧;
然後是符號“*”,因為棧頂“+”優先順序低於“*”,所以“*”入棧,後面數字3輸出,表示式變為“9 3 1 - 3”;
之後是符號“+”,因為棧頂“*”優先順序高於“+”,所以棧中元素出棧(因為沒有比“+”更低的優先順序,所以全部出棧),表示式變為“9 3 1 - 3 * +”,當前符號“+”入棧;
- 接著數字10,輸出,表示式變為“9 3 1 - 3 * + 10”;
然後“/”優先順序高於棧頂“+”,入棧;
最後是數字2,輸出,表示式為“9 3 1 - 3 * + 10 2”;
已經到了最後,所以將棧中符號全部出棧並輸出。最終輸出字尾表示式為“9 3 1 - 3 * + 10 2 / +”
計算字尾表示式
中綴轉字尾是將符號進出棧,計算字尾表示式就需要將數字進出棧。
規則就是將數字依次入棧,遇到運算子就出棧兩次,將棧頂元素分別作為被運算數和運算數進行計算,然後將計算結果入棧,直到表示式遍歷完成,運算結束,棧中存在的唯一元素就是表示式的值。
程式碼實現:
import java.util.Scanner;
import java.util.Stack;
public class JavaTest {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String infix = in.nextLine();
in.close();
String postfix = trans2postfix(infix);// 得到字尾表示式
System.out.println("postfix : " + postfix);
int count = calculate(postfix);
System.out.println("count = " + count);
}
// 計算字尾表示式
private static int calculate(String postfix) {
Stack<Integer> stack = new Stack<>();
char[] arr = postfix.toCharArray();
for (int i = 0; i < arr.length; i++) {
char current = arr[i];// 當前字元
if (Character.isDigit(current)) {// 如果是數字,入棧
stack.push(current - 48);
} else {
int n1 = stack.pop();
int n2 = stack.pop();
int m = 0;
switch (current) {
case '+':
m = n2 + n1;
break;
case '-':
m = n2 - n1;
break;
case '*':
m = n2 * n1;
break;
case '/':
m = n2 / n1;
break;
default:
break;
}
stack.push(m);
}
}
return stack.get(0);
}
// 中綴轉字尾
public static String trans2postfix(String infix) {
StringBuilder sb = new StringBuilder();
Stack<Character> stack = new Stack<>();
char[] arr = infix.toCharArray();
for (int i = 0; i < arr.length; i++) {
char current = arr[i];// 當前字元
if (Character.isDigit(current)) {// 如果是數字
sb.append(current);
} else {
if (stack.isEmpty()) {// 如果棧為空,直接入棧
stack.push(current);
} else {
if (current == ')') {// 閉括號
while (true) {
char top = stack.pop();
if (top != '(') {
sb.append(top);
} else {
break;
}
}
} else {
if (current != '(') {// 開括號,直接入棧
while (comparePriority(current, stack.peek()) <= 0) {// 當前優先順序不比棧頂高,出棧
char top = stack.pop();
sb.append(top);
if (stack.isEmpty()) {
break;
}
}
}
stack.push(current);
}
}
}
}
while (stack.size() != 0) {
sb.append(stack.pop());
}
return sb.toString();
}
// 比較運算子優先順序
private static int comparePriority(char arg0, char arg1) {
int pri0 = getPriority(arg0);
int pri1 = getPriority(arg1);
if (pri0 < pri1) {
return 1;
} else if (pri0 == pri1) {
return 0;
} else {
return -1;
}
}
// 獲取運算子優先順序
private static int getPriority(char arg0) {
switch (arg0) {
case '*':
case '/':
return 3;
case '+':
case '-':
return 4;
default:
return 0xF;
}
}
}
執行結果:
實現過程中遇到一個問題,使用char字元會將整數切成10以內的數字,比如10會變成1和0,而且字尾表示式也沒有做字元間隔,比如931無法區別是9、3、1還是93、1或者931,所以上面的程式碼只能計算10以內的四則運算。優化可以將char改成int,並且輸出要加上間隔。
能力有限,如有不足,請多指教。