大家看到,字尾表示式適合計算式進行運算,但是人卻不太容易寫出來,尤其是表示式很長的情況下,因此在開發中,我們需要將 中綴表示式轉成字尾表示式。
具體步驟如下:
- 初始化兩個棧:運算子棧s1和儲存中間結果的棧s2;
- 從左至右掃描中綴表示式;
- 遇到運算元時,將其壓s2;
- 遇到運算子時,比較其與s1棧頂運算子的優先順序:
- 如果s1為空,或棧頂運算子為左括號“(”,則直接將此運算子入棧;
- 否則,若優先順序比棧頂運算子的高,也將運算子壓入s1;
- 否則,將s1棧頂的運算子彈出並壓入到s2中,再次轉到(4-1)與s1中新的棧頂運算子相比較;
- 遇到括號時:(1) 如果是左括號“(”,則直接壓入s1(2) 如果是右括號“)”,則依次彈出s1棧頂的運算子,並壓入s2,直到遇到左括號為止,此時將這一對括號丟棄
- 重複步驟2至5,直到表示式的最右邊
- 將s1中剩餘的運算子依次彈出並壓入s2
- 依次彈出s2中的元素並輸出,結果的逆序即為中綴表示式對應的字尾表示式
>
舉例說明:
將中綴表示式“1+((2+3)×4)-5”轉換為字尾表示式的過程如下
因此結果為 "1 2 3 + 4 × + 5 –"
掃描到的元素 | s2(棧底->棧頂) | s1 (棧底->棧頂) | 說明 |
---|---|---|---|
1 | 1 | 空 | 數字,直接入棧 |
+ | 1 | + | s1為空,運算子直接入棧 |
( | 1 | + ( | 左括號,直接入棧 |
( | 1 | + ( ( | 同上 |
2 | 1 2 | + ( ( | 數字 |
+ | 1 2 | + ( ( + | s1棧頂為左括號,運算子直接入棧 |
3 | 1 2 3 | + ( ( + | 數字 |
) | 1 2 3 + | + ( | 右括號,彈出運算子直至遇到左括號 |
× | 1 2 3 + | + ( × | s1棧頂為左括號,運算子直接入棧 |
4 | 1 2 3 + 4 | + ( × | 數字 |
) | 1 2 3 + 4 × | + | 右括號,彈出運算子直至遇到左括號 |
- | 1 2 3 + 4 × + | - | -與+優先順序相同,因此彈出+,再壓入- |
5 | 1 2 3 + 4 × + 5 | - | 數字 |
到達最右端 | 1 2 3 + 4 × + 5 - | 空 | s1中剩餘的運算子 |
至於這個是怎麼想出來的,這個你得問那些禿頭的大佬
先寫一個方法將這個中綴表示式轉換為對應的list
//方法:將 中綴表示式轉成對應的List
// s="1+((2+3)×4)-5";
public static List<String> toInfixExpressionList(String s) {
//定義一個List,存放中綴表示式 對應的內容
List<String> ls = new ArrayList<String>();
int i = 0; //這個是一個指標,用於遍歷 中綴表示式字串
String str; // 對多位數的拼接
char c; // 每遍歷到一個字元,就放入到c
do {
//如果c是一個非數字,我需要加入到ls
if((c=s.charAt(i)) < 48 || (c=s.charAt(i)) > 57) {
ls.add("" + c);
i++; //i需要後移
} else { //如果是一個數,需要考慮多位數
str = ""; //先將str 置成"" '0'[48]->'9'[57]
while(i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57) {
str += c;//拼接
i++;
}
ls.add(str);
}
}while(i < s.length());
return ls;//返回
}
在寫一個方法
//即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]
//方法:將得到的中綴表示式對應的List => 字尾表示式對應的List
public static List<String> parseSuffixExpreesionList(List<String> ls) {
//定義兩個棧
Stack<String> s1 = new Stack<String>(); // 符號棧
//說明:因為s2 這個棧,在整個轉換過程中,沒有pop操作,而且後面我們還需要逆序輸出
//因此比較麻煩,這裡我們就不用 Stack<String> 直接使用 List<String> s2
//Stack<String> s2 = new Stack<String>(); // 儲存中間結果的棧s2
List<String> s2 = new ArrayList<String>(); // 儲存中間結果的Lists2
//遍歷ls
for(String item: ls) {
//如果是一個數,加入s2
if(item.matches("\\d+")) { // 這裡用一個正規表示式
s2.add(item);
} else if (item.equals("(")) {
s1.push(item);
} else if (item.equals(")")) {
//如果是右括號“)”,則依次彈出s1棧頂的運算子,並壓入s2,直到遇到左括號為止,此時將這一對括號丟棄
while(!s1.peek().equals("(")) {
s2.add(s1.pop());
}
s1.pop();//!!! 將 ( 彈出 s1棧, 消除小括號
} else {
//當item的優先順序小於等於s1棧頂運算子, 將s1棧頂的運算子彈出並加入到s2中,再次轉到(4.1)與s1中新的棧頂運算子相比較
//問題:我們缺少一個比較優先順序高低的方法
while(s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item) ) {
s2.add(s1.pop()); // 秒啊,將s1的拿出來放到s2裡面
}
//還需要將item壓入棧
s1.push(item);
}
}
//將s1中剩餘的運算子依次彈出並加入s2
while(s1.size() != 0) {
s2.add(s1.pop());
}
return s2; //注意因為是存放到List, 因此按順序輸出就是對應的字尾表示式對應的List
}
s2這個棧,沒有彈出棧的操作,所以不用也罷
這裡我們用ArrayList來替換掉,即可,哦哦
很多書上,他整個這個在為了講解這個棧,他
硬要用棧,就,就離譜,這裡我們靈活運用,嗯
本作品採用《CC 協議》,轉載必須註明作者和本文連結