先說一說中綴表達式。
中綴表達式也就是我們常用常說的算術表達式,像5*3, 3+4*6, (4+2)/6,(4-1)*(21+4) 這些都是中綴表達式。中綴表達式的特點是運算符在被運算的兩個數中間。
中綴表達式的運算對我們來說並不難,簡單的中綴表達式我們小學就會了。但對於電腦來說,運算中綴表達式卻並不簡單。電腦不懂加減乘除的優先級,如果再加幾個括號套過去套過來,只會把電腦搞的不要不要的~
這時候,我們就需要一種優秀的方法來解決這個問題了。於是就有了後綴表達式。
顧名思義,後綴表達式就是運算符在被運算的兩個數後面。像5*3寫成後綴表達式就是53*,4*2+3就是42*3+。後綴表達式沒有括號,運算符也沒有優先級,所有運算都是從左往右的。
後綴表達式的運算較爲簡單,從左往右依次讀取,如果讀到運算符,就把運算符前兩個數字執行相應運算,將結果保存到這個位置。比如4 2 * 3 +,先從左往右讀取,第三個遇到*,就把*前面的4和2拿出來相乘,得到8,把8放回去,後綴表達式就變成了8 3 +,再次讀取到+,8加上3等於11,就得到了4 2 * 3 +這個後綴表達式的值爲11.
後綴表達式沒有優先級,括號優先級這些都體現在它的存放順序中,它存放的順序直接決定了運算的順序。中綴表達式3+4*6可以表示成3 4 6 * +,也可以更直觀地表示成4 6 * 3 +。像中綴表達式(4+2)/6表示成後綴表達式就是4 2 + 6 /
電腦運算後綴表達式就很方便了,只需要從左往右跑一遍就可以了,而不需要像我們運算中綴表達式那樣跳過去跳過來的。如果我們要用電腦處理中綴表達式,可以先將中綴表達式轉化爲後綴表達式。
接下來就是重點難點了,中綴轉後綴。
中綴轉後綴要用到棧。
1.先寫簡單的中綴轉後綴,一位數的不包含括號的中綴表達式轉換:
將中綴表達式先保存到一個字符串a,再創一個字符串b用於儲存產生的後綴表達式,同時還需要一個用於存運算符的棧c。
這裏的運算符有個優先級的概念,有必要先說一下。每個運算符的優先級不同,我簡單歸納了一下:
+ | - | * | / | ( | ) | # |
2 | 2 | 3 | 3 | 1 | 4 | 0 |
這裏有個#號不是運算符,後面用的時候再說。
初始b和棧c都爲空,a裏面有一箇中綴表達式。然後從左往右依次讀取中綴表達式a的每一位,a[i]如果是數字就直接存進字符串b(這裏的數字都是一位數),如果是運算符就和棧頂元素比較,如果這個運算符的優先級大於或等於棧頂運算符的優先級,就把棧頂運算符取出來放進b字符串裏面,然後再用該運算符a[i]繼續和棧頂運算符進行比較,重複上面的操作,一直到找到優先級低於它的運算符或者棧爲空。從左往右一直把a字符串跑完一遍後,b裏面已經有一部分後綴表達式了,棧c裏面可能還有一部分運算符。那麼,我們接下來只需要把棧c裏面的運算符倒進b裏面就行了,也就是把棧c的元素依次出棧並存到字符串b裏面。這樣一個後綴表達式b就完成了。
這裏有個小技巧,可以在開始前先朝棧c裏面放一個字符#,字符#的優先級是最低的,沒有運算符能把它彈出來。這樣判斷的時候就不用判斷是否爲空棧了。
實戰轉換一下3*6+3。注意這裏的數字都是以字符的形式存的,所以運算的時候要減去個0字符。
首先創字符串a,b和棧c:
字符串a | a[i](操作的字符) | 操作 | 字符串b | 棧c |
3*6+3 |
3 | 3進b | 3 | NULL |
3*6+3 |
* | *進c | 3 | * |
3*6+3 |
6 | 6進b | 3 6 | * |
3*6+3 |
+ | *的優先級大於+,彈出*到b,c爲空,+進c | 3 6 * | + |
3*6+3 |
3 | 3進b | 3 6 * 3 | + |
a中處理完了 | 將c中元素倒入b | 3 6 * 3 + | NULL |
得到了它的後綴表達式爲3 6 * 3 +
這是最簡單的一種中綴轉後綴了,基礎理解好了後面就好說了。
2.然後就是有括號的中綴表達式,例如6*(4+2)/2
依然是從左往右讀,其他處理方式也一樣,只是兩個括號處理方式不同。讀到左括號(不用判斷直接存進棧c,不把其他運算符彈出棧。如果讀到右括號) 不用進棧,直接一個個把棧c的運算符出棧到b裏面,直到遇到到左括號( ,然後把左括號出棧扔了。繼續往後讀a[i]。
以6*(4+2)/2算一遍:
字符串a | a[i](操作的字符) | 操作 | 字符串b | 棧c |
6*(4+2)/2 |
6 | 6進b | 6 | NULL |
6*(4+2)/2 |
* | *進c | 6 | * |
6*(4+2)/2 |
( | (直接進c | 6 | *( |
6*(4+2)/2 |
4 | 4進b | 6 4 | *( |
6*(4+2)/2 | + | +進c | 6 4 | *(+ |
6*(4+2)/2 | 2 | 2進b | 6 4 2 | *(+ |
6*(4+2)/2 | ) | 取棧c的棧頂+,+進b,再取棧頂(,直接刪除 | 6 4 2 + | * |
6*(4+2)/2 | / | *的優先級等於/,*進b,c棧空,/進c | 6 4 2 + | / |
6*(4+2)/2 | 2 | 2進b | 6 4 2 + * 2 | */ |
a處理完畢 | c倒入b中 | 6 4 2 + * 2 / | NULL | |
所以,6*(4+2)/2的後綴表達式爲6 4 2 + * 2 /
3.然後再說多位數的。比如32+45*12這種。
關鍵是題目中的數字是以字符的形式儲存的,並不是他們真正的值。
這裏有兩種方法解決,一種是讀取的時候如果爲連續數字,則直接乘十往後加,也就是加出它真實的數字。比如32,先讀取3,然後讀取2,因爲是連續數字,所以3*10+2=32。這種方法比較方便,但應用實在有限,因爲是字符類型保存的,所以保存的值的大小不能超過256。並且這個數字還可能和運算符的ascll碼相同,導致最後當成了運算符處理。所以這方法其實沒什麼卵用~
第二種是普遍方法,更利於中綴轉後綴後輸出。簡單說就是在後綴表達式的每個數字完了後加上一個#,這樣會讓b字符串變成類似這樣的樣子:3 2 # 4 5 # 1 2 # * / 。這樣輸出的時候還需要稍作處理再輸出。
還是以32+45*12爲例寫一下吧:
字符串a | a[i](操作的字符) | 操作 | 字符串b | 棧c |
32+45*12 |
3 | 3進b | 3 | NULL |
32+45*12 | 2 | 2進b | 3 2 | NULL |
32+45*12 | + | +不爲數字,#進b,+進c | 3 2 # | + |
32+45*12 | 4 | 4進b | 3 2 # 4 | + |
32+45*12 | 5 | 5進b | 3 2 # 4 5 | + |
32+45*12 | * | *不爲數字,#進b,*進c | 3 2 # 4 5 # | +* |
32+45*12 | 1 | 1進b | 3 2 # 4 5 # 1 | +* |
32+45*12 | 2 | 2進b | 3 2 # 4 5 # 1 2 | +* |
a處理完了 | 最後一個爲數字,#進b,c倒入b | 3 2 # 4 5 # 1 2 # * + | NULL |
寫了幾個小時,終於寫完了~~
累~
字符串a | a[i](操作的字符) | 操作 | 字符串b | 棧c |
6*(4+2)/2 |
6 | 6進b | 6 | NULL |
6*(4+2)/2 |
* | *進c | 6 | * |
6*(4+2)/2 |
( | (直接進c | 6 | *( |
6*(4+2)/2 |
4 | 4進b | 6 4 | *( |
6*(4+2)/2 |
+ | +進c | 6 4 | *(+ |
6*(4+2)/2 |
2 | 2進b | 6 4 2 | *(+ |
6*(4+2)/2 |
) | 取棧c的棧頂元素+,+進b,再取棧頂元素( ,直接刪除 |
6 4 2 + | * |
字符串a | a[i](操作的字符) | 操作 | 字符串b | 棧c |
6*(4+2)/2 |
6 | 6進b | 6 | NULL |
6*(4+2)/2 |
* | *進c | 6 | * |
6*(4+2)/2 |
( | (直接進c | 6 | *( |
6*(4+2)/2 |
4 | 4進b | 6 4 | *( |
6*(4+2)/2 |
+ | +進c | 6 4 | *(+ |
6*(4+2)/2 |
2 | 2進b | 6 4 2 | *(+ |
6*(4+2)/2 |
) | 取棧c的棧頂元素+,+進b,再取棧頂元素( ,直接刪除 |
6 4 2 + | * |
字符串a | a[i](操作的字符) | 操作 | 字符串b | 棧c |
6*(4+2)/2 |
6 | 6進b | 6 | NULL |
6*(4+2)/2 |
* | *進c | 6 | * |
6*(4+2)/2 |
( | (直接進c | 6 | *( |
6*(4+2)/2 |
4 | 4進b | 6 4 | *( |
6*(4+2)/2 |
+ | +進c | 6 4 | *(+ |
6*(4+2)/2 |
2 | 2進b | 6 4 2 | *(+ |
6*(4+2)/2 |
) | 取棧c的棧頂元素+,+進b,再取棧頂元素( ,直接刪除 |
6 4 2 + | * |