前綴表達式、中綴表達式、后綴表達式


1.前綴表達式

前綴表達式是一種沒有括號的算術表達式,與中綴表達式不同的是,其將運算符寫在前面,操作數寫在后面。為紀念其發明者波蘭數學家Jan Lukasiewicz,前綴表達式也稱為“波蘭式”。例如,- 1 + 2 3,它等價於1-(2+3)。

運算優勢

編輯
前綴表達式是一種十分有用的表達式,將 中綴表達式轉換為前綴表達式后,就可以只依靠 出棧入棧兩種簡單操作完全解決中綴表達式的全部運算。
例如,(a+b)*(c+d)轉換為*,+,a,b,+,c,d。
后面的前綴表達式的運算方式為:如果當前字符(或字符串)為數字或變量,則壓入棧內;如果是 運算符,則將棧頂兩個元素彈出棧外並作相應運算,再將結果壓入棧內。當前綴表達式掃描結束時,棧里的就是中綴表達式運算的最終結果。對比中綴運算的步驟,不難發現前綴運算在計算機上的優勢。

求值方法

編輯
前綴表達式求值,要從右至左掃描表達式,首先從右邊第一個字符開始判斷,若當前字符是數字則一直到數字串的末尾再記錄下來,若為 運算符,則將右邊離得最近的兩個“數字串”作相應 運算,然后以此作為一個新的“數字串”並記錄下來;掃描到表達式最左端時掃描結束,最后運算的值即為 表達式的值。
例如:對 前綴表達式“- 1 + 2 3”求值,掃描到3時,記錄下這個數字串,掃描到2時,記錄下這個數字串,當掃描到+時,將+右移做相鄰兩數字串的運算符,記為2+3,結果為5,記錄下5這個新數字串,然后繼續向左掃描,掃描到1時,記錄下這個數字串,掃描到-時,將-右移做相鄰兩數字串的運算符,記為1-5,結果為-4,此時關於這個表達式的全部運算已完成,故表達式的值為-4。

表達對照

編輯
中綴表達式轉化為前綴表達式的例子:
a+b ---> +,a,b
a+(b-c) ---> +,a,-,b,c
a+(b-c)*d ---> +,a,*,-,b,c,d
a+1+3 ---> +,+,a,1,3

轉換算法

編輯
(1) 首先構造一個 運算符棧(也可放置括號),運算符(以括號為分界點)在棧內遵循越往棧頂優先級不降低的原則進行排列。
(2)從右至左掃描 中綴表達式,從右邊第一個字符開始判斷:
如果當前 字符是數字,則分析到數字串的結尾並將數字串直接輸出。
如果是運算符,則比較 優先級。如果當前運算符的優先級大於等於棧頂運算符的優先級(當棧頂是括號時,直接入棧),則將 運算符直接入棧;否則將棧頂運算符 出棧並輸出,直到當前運算符的優先級大於等於棧頂運算符的優先級(當棧頂是括號時,直接入棧),再將當前運算符入棧。
如果是括號,則根據括號的方向進行處理。如果是向右的括號,則直接入棧;否則,遇向左的括號前將所有的運算符全部出棧並輸出,遇右括號后將向左、向右的兩括號一起出棧(並不輸出)。
(3) 重復上述操作(2)直至掃描結束,將棧內剩余運算符全部出棧並輸出,再逆綴輸出字符串。 中綴表達式也就轉換為前綴表達式了。

 

 

實例分析

編輯
中綴表達式“1+((2+3)*4)-5”轉換為前綴表達式。
中綴表達式
前綴表達式
(棧尾)運算符棧(棧頂)
說明
5
5
5,是數字串直接輸出
-
5
-
-,棧內無運算符,直接入棧
5
-)
),直接入棧
4
5 4
-)
4,是數字串直接輸出
*
5 4
-)*
*,棧頂是括號,直接入棧
)
5 4
- ) * )
),直接入棧
3
5 4 3
- ) * )
3,是數字串直接輸出
+
5 4 3
- ) * ) +
+,棧頂是括號,直接入棧
2
5 4 3 2
- ) * )+
2,是數字串直接輸出
(
5 4 3 2+
- ) *
(,與棧里最后一個)抵消,並釋放它們之間的+
(
5 4 3 2+*
-
(,方法與上類同,請參考下一目錄
+
5 4 3 2+*
-+
+,優先級 大於等於棧頂運算符,直接入棧
1
5 4 3 2+*1
-+
1,是數字串直接輸出
5 4 3 2+*1+-
掃描結束,將棧內剩余運算符全部出棧並輸出
- + 1 * + 2 3 4 5
逆綴輸出字符串

符號處理

編輯
對運算符的具體處理方法如下:
):直接入棧
(:遇)前,將運算符全部出棧並輸出;遇)后,將兩括號一起刪除①
+、-:1
*、/、%:2
^:3

2.后綴表達式

 

一個 表達式E的后綴形式可以如下定義:

 

(1)如果E是一個變量或 常量,則E的 后綴式是E本身。

 

(2)如果E是E1 op E2形式的表達式,這里op是任何二元操作符,則E的后綴式為E1'E2' op,這里E1'和E2'分別為E1和E2的后綴式。

 

(3)如果E是(E1)形式的表達式,則E1的后綴式就是E的后綴式。

 

如:我們平時寫a+b,這是 中綴表達式,寫成 后綴表達式就是:ab+

 

(a+b)*c-(a+b)/e的后綴表達式為:

 

(a+b)*c-(a+b)/e

 

→((a+b)*c)((a+b)/e)-

 

→((a+b)c*)((a+b)e/)-

 

→(ab+c*)(ab+e/)-

 

→ab+c*ab+e/-

 

 

算法實現

編輯
將一個普通的中綴表達式轉換為 逆波蘭表達式的一般算法是:
首先需要分配2個棧,一個作為臨時存儲運算符的棧S1(含一個結束符號),一個作為輸入逆波蘭式的棧S2(空棧),S1棧可先放入優先級最低的運算符#,注意,中綴式應以此最低優先級的運算符結束。可指定其他字符,不一定非#不可。從中綴式的左端開始取字符,逐序進行如下步驟:
(1)若取出的字符是 操作數,則分析出完整的運算數,該操作數直接送入S2棧
(2)若取出的字符是 運算符,則將該運算符與S1棧棧頂元素比較,如果該 運算符優先級(不包括括號運算符)大於S1棧棧頂運算符優先級,則將該運算符進S1棧,否則,將S1棧的棧頂運算符彈出,送入S2棧中,直至S1棧棧頂運算符低於(不包括等於)該運算符優先級,最后將該運算符送入S1棧。
(3)若取出的字符是“(”,則直接送入S1棧頂。
(4)若取出的字符是“)”,則將距離S1棧棧頂最近的“(”之間的運算符,逐個 出棧,依次送入S2棧,此時拋棄“(”。
(5)重復上面的1~4步,直至處理完所有的輸入字符
(6)若取出的字符是“#”,則將S1棧內所有運算符(不包括“#”),逐個出棧,依次送入S2棧。
完成以上步驟,S2棧便為逆波蘭式輸出結果。不過S2應做一下逆序處理。便可以按照逆波蘭式的計算方法計算了!

 

 

 

 

 

 

 代碼實現

public class PolandNotation {
    public static void main(String[] args) {
        //創建逆波蘭表達式
        //(3+4)*5-6   => 3 4 + 5 * 6 -
       /* String suffixExpression= "3 4 + 5 * 6 -";
        *//*1.將3 4 + 5 * 6 -  =》放到List中
        * 2.將list利用方法遍歷list配合棧進行計算*//*
        List<String> rpnlist = getList(suffixExpression);
        System.out.println("rpnlist=" +rpnlist);
        int res = calculation(rpnlist);
        System.out.printf("計算結果: %d\n",res);*/


        /*將中綴表達式轉換成后綴表達式*/
        //1+((2+3)*4)-5 ====>1 2 3 + 4 * + 5 -
        /*將字符串轉換成中綴的list*/
        String expression =  "1+((2+3)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("后綴表達式對應的List=" + infixExpressionList);
        List<String> parseSuffixExpressionList = parseSuffixExpressionList(infixExpressionList);
        //結果List=[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
        System.out.println("后綴表達式對應的List= " + parseSuffixExpressionList);
        //結果 List= [1, 2, 3, +, 4, *, +, 5, -]
        int calculation = calculation(parseSuffixExpressionList);
        System.out.printf("計算結果為:%d\n",calculation);//=16

    }

    /*將字符串轉換成中綴的list*/
    public static List<String> toInfixExpressionList(String s){
        List<String> ls= new ArrayList<>();
        int i=0;//指針:遍歷中綴字符串
         String str;//多位數拼接
        char c;//保存字符 放入c;
        do {
            //c是數字:進行字符串拼接
            if ((c=s.charAt(i))<48 || (c=s.charAt(i))>57){
                ls.add(""+c);
                i++;
            }else {//考慮多位數問題
                str="";//
                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;
    }



    /*中綴表達式轉成后綴表達式*/
    public  static  List<String> parseSuffixExpressionList(List<String> ls){
        Stack<String> s1 = new Stack<>(); //符號棧
        //第二個棧不需要pop操作,而且需要逆向輸出所以利用list代替
        List<String> s2 = new ArrayList<>();
        for (String item : ls) {
            /*如果是數入s1*/
            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();//將左括號丟棄消除小括號
            }else {
                /*當item的優先級小於等於s1棧頂的運算符的優先級
                * 1.將s1的運算符彈出並加入到s2中,依次比較新的運算符
                * 方法比較運算符的優先級的方法*/
                while (s1.size()!=0 && Operation.getValue(s1.peek())>=Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                //將item壓入棧中
                s1.push(item);
            }
        }
        /*將s1剩余的運算符壓入棧中*/
        while (s1.size()!=0){
            s2.add(s1.pop());
        }
        return s2;//存放到list,因此按順序輸出就是逆波蘭表達式
    }












    /*將逆波蘭表達式放入到list*/
    public  static List<String> getList(String suffixExpression){
        //字符串分割
        String[] split = suffixExpression.split(" ");
        List<String> list = new ArrayList<>();
        for (String ele : split) {
            list.add(ele);
        }
     return list;
    }

    /*完成逆波蘭運算*/
    public  static  int calculation(List<String> ls){
        //創建一個棧
        Stack<String> stack = new Stack<String>();
        for (String item : ls) {
            /*使用正則表達式*/
            if (item.matches("\\d+")){
                stack.push(item);//入棧
            }else {
                int num2 =Integer.parseInt(stack.pop());//取出pop2個數據進行運算再入棧
                int num1 =Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")){
                    res = num1+num2;
                }else if (item.equals("-")){
                    res = num1-num2;
                }else if (item.equals("*")){
                    res= num1*num2;
                }else if (item.equals("/")){
                    res = num1/num2;
                }else {
                    throw  new RuntimeException("運算符號錯誤");
                }
                //將結果入棧
                stack.push(res+"");
            }
        }
        //留着棧中的就是計算結果
        return Integer.parseInt(stack.pop());
    }


}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM