c push 應輸入表示式 用棧進行表示式求值

2021-10-13 18:17:19 字數 3251 閱讀 2061

我們今天繼續看一下,如何使用棧完成標準的四則混合運算表示式求值。

不同於字尾表示式,遇到乙個運算子就可以直接從棧裡取兩個數進行運算。在標準的四則混合運算表示式中(或者我們稱之為中綴表示式),遇到乙個操作符是不能直接計算的,因為計算的順序要取決於後面的運算子。多舉幾個例子,大家就能明白了。由於加和減是相同的運算優先順序,乘和除是相同的運算優先順序,我們就只用加和乘來舉例就可以了。

我們像計算字尾表示式一樣,引入乙個運算元棧。由於字尾表示式不用快取 +-*/ 這些操作符,所以乙個運算元棧就夠了。但是,中綴表示式的計算是要用到操作符的快取的,所以,我們還要再引入乙個操作符棧,專門儲存各個操作符。

第乙個例子:a + b * c,我們從前向後掃瞄,遇到a,壓到運算元棧裡,遇到 + ,壓到操作符棧裡,下乙個是 b,此時,我們是不能像字尾表示式求值那樣,直接計算 a + b,然後壓棧的。因為我們不確定,b 是否會先參與後面的運算,所以我們只能把 b 先壓棧,繼續向後掃瞄。看到 * 以後,再把 * 壓到操作符棧裡;看到 c 以後,也要把 c 先壓棧,理由與 b 壓棧相同。接下來,再往下掃瞄,就發現已經到了末尾了。那我們就可以放心地從操作符棧裡取出頂上的操作符,在這個例子中,就是 *,然後再從運算元棧裡取出兩個運算元,就是 b 和 c,然後把b和c的積,不妨記為d,放到運算元棧裡。接下來,再去看操作符棧裡,還有乙個 +,那就把 + 取出來,然後去運算元棧裡取出 a 和 d,計算它們的和,這就是最終的結果了。

第二個例子:a + b + c,我們從前向後掃瞄,遇到a,壓到運算元棧裡,遇到 + ,壓到操作符棧裡,下乙個是 b,壓到運算元棧裡。再下乙個,又是 + ,由於加法的結合律,我們知道,先把a + b 的結果計算出來,或者後計算,都不會影響最終的結果。所以我們就把能做的化簡先做掉。就是說,在掃瞄到第二個操作符是 + 以後,我們就把第乙個操作符取出來,再把兩個運算元取出來,求和並且把和送到運算元棧裡。接下來的過程與第乙個例子是相同的,不再贅述了。

通過這兩個例子,我們看到,乙個操作符究竟什麼時候進行運算,並不取決於它前面的那個操作符是什麼,而是取決於它後面的那個操作符是什麼。更具體一點講:如果後面的操作符的運算優化級比前面的操作符高,那麼前面的操作符就必須延遲計算;如果後面的操作符優化級比前面的低或者相等,那麼前面的操作符就可以進行計算了。上面這句話,非常重要,是我們這節課的核心。請多讀幾遍,結合上面的兩個例子,務必想明白它。

好了,理解了這個,就可以上**了。

先看 stack的完整定義:

class stack  public t gettop()  public void push(t t)  public t pop()  public boolean isempty() }
然後我們建立兩個棧,乙個是運算元棧,乙個是操作符棧:

public class stackexpression }
其中,tokenstream 是這節課裡的課後作業:介面卡模式

按照上面分析的演算法,如果遇到數字,就壓棧到numbers裡,如果遇到操作符,就要看前面乙個的操作符的優先順序是否比當前操作符高,如果前乙個操作符高,那麼執行前乙個操作符的操作,如果是後面的高,那就只要把後面的操作符壓棧即可:

public class stackexpression  else  else  } } while (!operators.isempty())  system.out.println("result is " + numbers.gettop()); } private static void binarycalc(stack numbers, stack operators)  private static int preorder(token.tokentype left, token.tokentype right)  else return 1; }}
好了。我們的這個程式已經可以處理類似 a + b - c * d + e / f 這樣的算式了。

為了讓大家看得更清楚,我用圖把 a + b * c / d 的過程畫一遍。

首先,遇到 a ,把 a 送到運算元棧,遇到 + ,送到操作符棧:

遇到 b,壓棧

遇到乘,由於乘的優先順序高於加,所以,現在就什麼也不做,只把乘號進棧:

同樣,遇到 c 把 c 進棧(此圖略,請自己補上),再遇到 / ,由於除的優先順序與 * 的優先順序相同,所以,乘就可以先做了。這個動作是把乘號出棧,把c 和 b出棧,求 c * b的值,並且把這個值入棧。即:

然後把 / 入棧,把 d 入棧:

現在到了運算的結尾了。我們只需要把現在的棧裡的內容從頂向下計算起來即可,先算除法:

再算加法:

但是,括號怎麼辦?

可以這樣想,在遇到形如 a * (b + c) 這樣的形式的時候,左邊的乘法是一定不能做的,我們只需要將左括號進棧即可。所以,我們可以把tokenstream裡取得的左括號看做是乙個優先順序無窮大的運算子,它使得左方的運算子都不能提前進行計算。

遇到右括號時,b + c 這個加法是可以進行運算的了,所以可以把右括號看作是乙個優先順序無窮小的運算子,它會使得操作符棧上的所有運算子都出棧並執行計算,直到遇到左括號。遇到左括號以後,只需要把左括號從棧裡彈出來,然後讓它和右括號一起狗帶即可(這就是括號匹配啊,同學們~)。由於括號內的計算都已經完成了,結果是乙個整數,我們已經在計算的過程中把這個整數放到運算元棧裡了。所以整個括號內的求值就完成了。

好了。今天的課程很短,但是比較難理解,尤其是,今天的程式都依賴於本周前邊的課程。請務必先完成本週前邊的課程再來看這節課。

演示一下,我的完整版的效果:

用棧計算表示式

首先宣告我們的表示式 expression 只是簡單的四則運算,再加上小括號.運算元operand,操作符operator 如果直接給出字尾表示式 postfix,也叫逆波蘭式 是最容易計算的,這種表示式中已經不含括號.方法 遇到運算元時壓入棧中 遇到操作符時從棧中彈出兩個元素進行此運算,再將運算結...

用棧計算表示式

首先宣告我們的表示式 expression 只是簡單的四則運算,再加上小括號.運算元operand,操作符operator 如果直接給出字尾表示式 postfix,也叫逆波蘭式 是最容易計算的,這種表示式中已經不含括號.方法 遇到運算元時壓入棧中 遇到操作符時從棧中彈出兩個元素進行此運算,再將運算結...

C 用棧計算表示式

基本思路 1 設定兩個棧,乙個運算子棧,乙個運算元棧。初始化後將 壓入操作符棧中。2 順序掃瞄 當輸入為運算元時就將其壓入運算元棧。當輸入為運算子時,則比較輸入運算子和運算子棧的棧頂運算子的優先順序的大小。若輸入運算子的優先順序高於運算子棧頂運算子的優先順序時,則將其壓入到運算子棧 若運算子棧頂運算...