逆波蘭表達式

本文轉載自:https://www.cnblogs.com/wanghetao/archive/2012/04/23/2466580.html 作者:wanghetao 轉載請註明該聲明。

逆波蘭表達式

 
表達式一般由操作數(Operand)、運算符(Operator)組成,例如算術表達式中,通常把運算符放在兩個操作數的中間,
這稱爲中綴表達式(Infix Expression),如A+B。
波蘭數學家Jan Lukasiewicz提出了另一種數學表示法,它有兩種表示形式:
把運算符寫在操作數之前,稱爲波蘭表達式(Polish Expression)或前綴表達式(Prefix Expression),如+AB;
把運算符寫在操作數之後,稱爲逆波蘭表達式(Reverse Polish Expression)或後綴表達式(Suffix Expression),如AB+;
其中,逆波蘭表達式在編譯技術中有着普遍的應用。
 
算法:
一、 將中綴表達式轉換成後綴表達式算法:
1、從左至右掃描一中綴表達式。
2、若讀取的是操作數,則判斷該操作數的類型,並將該操作數存入操作數堆棧
3、若讀取的是運算符
  (1) 該運算符爲左括號"(",則直接存入運算符堆棧。
  (2) 該運算符爲右括號")",則輸出運算符堆棧中的運算符到操作數堆棧,直到遇到左括號爲止。
  (3) 該運算符爲非括號運算符:
      (a) 若運算符堆棧棧頂的運算符爲括號,則直接存入運算符堆棧。
      (b) 若比運算符堆棧棧頂的運算符優先級高或相等,則直接存入運算符堆棧。
      (c) 若比運算符堆棧棧頂的運算符優先級低,則輸出棧頂運算符到操作數堆棧,並將當前運算符壓入運算符堆棧。
4、當表達式讀取完成後運算符堆棧中尚有運算符時,則依序取出運算符到操作數堆棧,直到運算符堆棧爲空。
 
二、逆波蘭表達式求值算法:
1、循環掃描語法單元的項目。
2、如果掃描的項目是操作數,則將其壓入操作數堆棧,並掃描下一個項目。
3、如果掃描的項目是一個二元運算符,則對棧的頂上兩個操作數執行該運算。
4、如果掃描的項目是一個一元運算符,則對棧的最頂上操作數執行該運算。
5、將運算結果重新壓入堆棧。
6、重複步驟2-5,堆棧中即爲結果值。
 
 
 

  C語言講解【複合複製運算符】時會提到,複合運算簡練並且產生機器碼的效率高。爲什麼效率高呢,

這就必須要了解:【編譯原理】中提到的【逆波蘭式】。逆波蘭表達式又叫做【後綴表達式】。推而廣之必然還

存在【前綴表達式】、【中綴表達式。】(有時也成爲,前序式,中序式,後序式)。中綴表達式就是我們常用的

所謂的標準的表達式如"A+B",它在數學上學名叫中綴表達式(Infix Notation),原因是:

運算符號在兩個運算對象的中間

      其優勢: 在於只:用兩種簡單操作,入棧和出棧就可以搞定任何普通表達式(僅包含:+-*/和()的表達式)的運算。

      其基本運算方式 :如果當前字符爲變量或者爲數字,則壓棧,如果是運算符,則將棧頂兩個元素彈出作相應運算,結果再入棧,最後當表達式掃描完後,棧裏的就是結果。

      爲什麼說逆波蘭式產生機器碼的效率高因爲逆波蘭式非常易於計算機的處理。原因是這樣的。舉例:

     3   32   + 5 3   *   -
  12 34 2   -   *   8 /
  乍一看上面兩個式子很奇怪,是嗎?它們就是一種表達式的記法——逆波蘭表達式。
  現在,準備一個很窄的圓筒,筒是有底的,像一個細長的杯子,粗細剛好和一枚硬幣相當。再做幾個和硬幣一樣大的小圓紙片,在紙片上依次寫上“3”“32”“+”“5”“3”“*”“-”,記住,每個紙片上要麼只寫一個數,要麼只寫一個運算符號,把它們按上面的順序排好。好,現在仔細聽我說,按順序一個接一個地拿起小圓紙片,反覆執行以下幾個規則:
  1. 如果你拿着的是一個數,不多說,直接把它放進圓筒;
  2. 如果你拿着的是一個運算符號,不要把它放進去。先從圓筒裏取出兩個數(當然是先取最上而的啦,筒很細的),然後處這兩個數作運算符號指定的運算,並把結果寫在一張新的紙片上,然後放進筒裏。比如你拿着的是“+”,你要依次取出“32”和“3”,讓它們相加,得“35”,把“35”寫在一張新紙片上(現在“34”和“12”可以扔掉了),並把這張新紙片放進圓筒。
  當圓筒裏只有一個數時,你就可以停下來了,我猜這個數是20,沒錯,這就是這個表達式的值!
  我們剛纔操作的,其實就是一個“棧”,棧是一種數據結構,具有一個性質——後進先出(LIFO——Last Input First Output),你已經深有體會了,就像一摞盤子,你只能從最上面的開始取,放的時候也只能放在最上面。放進去的動作叫做“入棧”,取出來叫做“彈出”。以後你就可以把棧想像成一摞盤子,或是上面說的小圓筒和小紙片,棧就是這麼簡單
  逆波蘭表達式雖然看起來比較繁瑣,其實在計算機中很有用。計算機可不知道先乘除後加減,先括號內後括號外,它要把你輸入的式子變成逆波蘭表達式,它就可以不斷地執行上面兩個固定的規則,直至把結果算出來告訴你。編譯器在處理時候按照從左至右的順序讀取逆波蘭表達式,遇到運算對象直接壓入堆棧,遇到運算符就從堆棧提取後進的兩個對象進行計算,這個過程正好符合了計算機計算的原理。所以,逆波蘭式非常適宜計算機的處理。

       那麼,接下來的問題就是:

      將一箇中綴表達式 轉換成 逆波蘭式的算法 結合一個具體例子分析如下:

a)給出一箇中綴表達式1*(2+3)

 

b)系統先定義兩個先進後出的堆棧:運算符號棧(簡稱入棧in),後綴表達式輸出符號棧(簡稱出棧out)

 

c)系統按從左至右的順序讀取中綴表達式

 

d)讀入數字直接壓入出棧(out)

 

e)讀入第一個運算符直接壓入入棧(in)

 

f)讀入"("直接壓入入棧(in)。 按上述規則讀取若干次後,若,此時兩棧的數據爲: in 【*,( 】 ; out 【1,2】,開始讀取的第二個的運算符"+",並將之與入棧(in)中的棧頂運算符"("進行比較,

g)高於棧頂運算符級別的算符直接進棧,低於或等於棧頂級別的要將入棧(in)解棧(即出棧),按次壓入出棧(out)中。比如現在入棧的運算順序爲(,*,/,此時若系統讀取的運算符爲+,級別比/要低,此時要按/,*的順序壓入出棧out中,並在入棧中釋放/和*符號,最後得到 in ( ; out /,*的結果。

f)最後讀取")"時要找到入棧in中最近的"(",將其前面所有符號全部按後進先出的順序壓入出棧,並解壓,"("與")"抵消。此時兩棧的數據爲:in 1,2,3,+ ; out *

 

g)系統讀取中綴表達式結束後將入棧in中的所有符號按後進先出的順序全部解壓,並依次壓入出棧out中,最後出棧的結果就應該爲1,2,3,+,*

 

h)按先進先出的順序將出棧out解壓得到後綴標準表達式1,2,3,+,*

 

兩個堆棧先後數據情況:

In

out

 

1

*

1

*,(

1

*,(

1,2

*,(,+

1,2

*,(,+

1,2,3

*

1,2,3,+

 

1,2,3,+,*

 

將中綴表達式轉換成逆波蘭表達式過程中,特別要注意對於中綴標到式中括號的處理

1、要注意的,如果算符是"(",無論入棧中棧頂級別(只看棧頂)爲何直接入棧,所以,“(”的等級

    只用於對其後入棧的算符進行優先級比較,在“(”入棧時是無視優先級的。

    注: 逆波蘭用的優先級有以下幾種:   等級從高-->低 是:   1、* \       2、+ -       3、(        4、)

2、在遇到")"時候找到最後進入的"(",並把"("前面所有的符號都壓入出棧。不能僅憑運算符的級別來判斷。

 

將一個 逆波蘭式 倒轉回 中綴表達式 的算法:

   這個就相當簡單了,就是一個機械的入堆棧出堆棧的操作,

1)設置一個堆棧,將逆波蘭式從左到右開始進行出入堆棧操作,還以上例爲例:1,2,3,+,*

2)遇到數字直接壓棧;例如,上例逆波蘭先進行三次入棧操作,堆棧的格局是: 1,2,3(棧頂);

3)遇到算符,將堆棧中的兩個數字出棧。 如,讀到+號後,2,3出棧,進行運算。注意,出棧時先出棧的元素是右算子,後出棧的是左算子,上例是2+3,不是3+2;

4)將運算的結果作爲新的算子,壓入堆棧中。如運算結果(2+3)入棧,堆棧格局:1,(2+3);

5)反覆1-4的操作,得到的中序表達式就是: 1*(2+3);

   中序表式生成的逆波蘭式唯一嗎?:

是唯一的,和固定形式的中序表達式一一對應,但,請注意這個概念,

例如: a+(b-c)*d 和 (b-c)*d+a 和 a+d*(b-c) 的是完全一樣的。但是,他們的中序形式不同,

產生的逆波蘭式必然是不同的。

      a+(b-c)*d : abc-d*+

      (b-c)*d+a :   bc-d*a+

      a+d*(b-c) : adbc-*+

發佈了0 篇原創文章 · 獲贊 49 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章