藍橋杯——表示式計算(支援多位數運算的java實現)
表示式長度不超過100,表示式運算合法且運算過程都在int內進行。
其實要做出這道題,首先要知道如何將中綴表示式轉換成字尾表示式,還有就是如何計算字尾表示式的結果;能夠正確的計算出例如:樣例輸入1-2+3*(4-5)的字尾表示式並且算出其結果那麼這倒題其實已經快要做出來了
中綴表示式1-2+3*(4-5)的字尾表示式就是12-345-*+;其結果是-4;
下面就是在java如何將中綴表示式轉換成字尾表示式,以及如何計算字尾表示式的原則和方法:
1.將中綴表示式轉換為字尾表示式的方法:
(1) 初始化兩個棧:運算子棧S1和儲存中間結果的棧S2;
(2) 從左至右掃描中綴表示式;
(3) 遇到運算元時,將其壓入S2,這裡由於運算數可能大於10,所以如果數字後面一個符號是運算子,則將‘#’入S2棧充當分割線;
(4) 遇到運算子時有三種情況:
(4-1) 三種情況下直接入S1棧①S1為空②運算子為‘(’③運算子優先順序比S1棧頂運算子的高;
(4-2)如果右括號“)”,則依次彈出S1棧頂的運算子,並壓入S2,直到遇到左括號為止,此時將這一對括號丟棄;
(4-3) 若運算子優先順序小於或等於S1棧頂運算子的優先順序,則依次彈出S1棧頂元素,直到運算子的優先順序大於S1棧頂運算子優先順序
(6) 重複步驟(2)至(5),直到表示式的最右邊;
(7) 將S1中剩餘的運算子依次彈出並壓入S2;
(8) 依次彈出S2中的元素並輸出,結果的逆序即為中綴表示式對應的字尾表示式。
運算子優先順序: 左括號>(乘=除)>(加=減)>右括號 為了程式設計方便假定右括號優先順序最小。
tip:“(”當成一個特殊的運算子來處理,即遇到“(”直接入棧,如果棧頂元素是“(”,那麼由於我們將“(”的優先順序設成了最高的,
那麼如果不做出相應的特殊處理那麼“(”將會被彈出,這不是我們想看到的,所以我們設定如果棧頂元素是“(”那麼下一個運算子無論
其優先順序的高低,都直接入棧。
2.字尾表示式計算方法:
(1)定義一個int棧S3,定義一個整形陣列num用來儲存大於10的數字便於計算,從左至右掃描表示式。
(2)遇到數字時:
(2-1)若數字後面一個元素不是#(數字後面只可能是#或數字)則將數字字元轉化為數字存在num[ ]陣列中;
(2-2)若數字後面一個元素是#,將num陣列中儲存的數字算出來並壓入S3棧中。
(3)遇到運算子時,彈出S3棧頂的兩個數,用運算子對它們做相應的計算(次頂元素 op 棧頂元素),並將結果入棧;重複上述過程直到表示式最右端,最後運算得出的值即為表示式的結果。
import java.util.Scanner;
import java.util.Stack;
public class CountExperssion {
private static Stack<Integer> stackN = new Stack<Integer>();// 數字
private static Stack<Character> stackF = new Stack<Character>();// 符號
private static Stack<Character> stackZ = new Stack<Character>();// 中間
private static char [] ch;
private static char [] ch1;// 中間字元陣列
private static int [] num = new int[10];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.next();
ch = str.toCharArray();
int result = countHou(zhongToHou(ch));
System.out.println(result);
in.close();
}
public static int compareYXJ(char c){// 用來輸出運算子的優先順序
if(c == ')'){
return 1;
}else if(c == '+' || c == '-'){
return 2;
}else if(c == '*' || c == '/'){
return 3;
}else if(c == '('){
return 4;
}
return 999;
}
public static int operation(int a, int b, char c){// 計算方法
if(c == '+'){
return a + b;
}else if(c == '-'){
return b - a;
}else if(c == '*'){
return a * b;
}else if(c == '/' && a != 0){
return b / a;
}
return -1;
}
public static char[] zhongToHou(char [] ch){// 中綴表示式轉換成字尾表示式
int n = ch.length;
String str = "";
for(int i = 0; i < n; i++){
if(ch[i] >= '0' && ch[i] <= '9'){// 判斷是否是數字
if(i + 1 < n && (ch[i+1] < '0' || ch[i+1] > '9') || i + 1 == n){// 如果一個數字的後面是運算子
stackZ.push(ch[i]);
stackZ.push('#');
}else{// 如果是數字的情況
stackZ.push(ch[i]);
}
}else{// 是運算子
if(stackF.isEmpty() || ch[i]=='(' || compareYXJ(ch[i]) > compareYXJ(stackF.peek())){// 如果運算子棧為空或者該運算子比棧頂運算子的優先順序高的時候直接入棧
stackF.push(ch[i]);
}else if(ch[i] == ')'){// 當是右括號的時候
while(stackF.peek() != '('){
stackZ.push(stackF.pop());
}
stackF.pop();
}else{// 優先順序低於棧頂運算子的時候
while(!stackF.isEmpty() && compareYXJ(ch[i]) <= compareYXJ(stackF.peek()) && stackF.peek() != '('){
stackZ.push(stackF.pop());
}
stackF.push(ch[i]);
}
}
}
while(!stackF.isEmpty()){// 當表示式走完之後將符號棧中的符號全部彈出壓入中間數字棧
stackZ.push(stackF.pop());
}
while(!stackZ.isEmpty()){// 將中間棧中的值全部彈出得到一個倒轉的字尾表示式
str += stackZ.pop()+"";
}
ch1 = str.toCharArray();
int a = ch1.length;
for(int i = 0; i < a/2; i++){// 該演算法將ch1反轉
char t;
t = ch1[i];
ch1[i] = ch1[a-1-i];
ch1[a-1-i] = t;
}
return ch1;
}
public static int countHou(char [] ch){// 計算字尾表示式
int n = ch.length;
int sum = 0;
int k = 0;
int tmp = 0;
for(int i = 0; i < n; i++){
if(ch[i] == '#'){
continue;
}else if(ch[i] == '+' || ch[i] == '-' || ch[i] == '*' || ch[i] == '/'){// 如果是運算子,則彈出連個數字進行運算
sum = operation(stackN.pop(), stackN.pop(), ch[i]);
stackN.push(sum);
}else{// 如果是數字
if(ch[i+1] == '#'){// 如果下一個是‘#’
num[k++] = ch[i] - '0';
for(int j = 0; j < k; j++){
tmp += (num[j] * (int)Math.pow(10, k-j-1));
}
stackN.push(tmp);
tmp = 0;
k = 0;
}else{// 下一個元素是數字
num[k++] = ch[i] - '0';
}
}
}
return stackN.peek();
}
}