中綴轉字尾表示式思路分析和程式碼實現

victorfengming發表於2021-03-04

大家看到,字尾表示式適合計算式進行運算,但是人卻不太容易寫出來,尤其是表示式很長的情況下,因此在開發中,我們需要將 中綴表示式轉成字尾表示式。

具體步驟如下:

  1. 初始化兩個棧:運算子棧s1和儲存中間結果的棧s2;
  2. 從左至右掃描中綴表示式;
  3. 遇到運算元時,將其壓s2;
  4. 遇到運算子時,比較其與s1棧頂運算子的優先順序:
    1. 如果s1為空,或棧頂運算子為左括號“(”,則直接將此運算子入棧;
    2. 否則,若優先順序比棧頂運算子的高,也將運算子壓入s1;
    3. 否則,將s1棧頂的運算子彈出並壓入到s2中,再次轉到(4-1)與s1中新的棧頂運算子相比較;
  5. 遇到括號時:(1) 如果是左括號“(”,則直接壓入s1(2) 如果是右括號“)”,則依次彈出s1棧頂的運算子,並壓入s2,直到遇到左括號為止,此時將這一對括號丟棄
  6. 重複步驟2至5,直到表示式的最右邊
  7. 將s1中剩餘的運算子依次彈出並壓入s2
  8. 依次彈出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中剩餘的運算子

img

至於這個是怎麼想出來的,這個你得問那些禿頭的大佬

先寫一個方法將這個中綴表示式轉換為對應的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 協議》,轉載必須註明作者和本文連結
秋葉夏風

相關文章