數據結構之棧的應用----算術表達式的實現

這次所講的是又一個棧的應用:算術表達式

這個程序是棧的應用的典型例子,是很好的棧的詮釋,這個程序在K&R的書上也被叫做逆波蘭計算器


/**********算術表達式的設計思想***********/

(1)首先,根據四則運算表達式,我們要先了解到其計算規則:括號爲界限符,其優先率最高,其次爲運算符*和/,最後爲+和-;

(2)然後,我們根據把一個算術表達式劃分爲:開始('#')、運算符(加減乘除)、操作數(數值)和界限符(一對原括號)的方法,創建兩個棧,

一個爲Operator(運算符)棧,另一個爲Operand(操作數)棧

<1>先將'#'push到Operator中,它將作爲算術表達式的開始和結束標誌

<2>我們依次把算術表達式的字符串給push如棧中,遇到數字時,就把它push到Operand中,這個比較好做,但是當遇到運算符時,我

們需要幾個判斷,後面會有介紹

<3>原括號的特殊處理(後面會說到)

<4>最後想說的是,我是先將整個表達式置於一個字符串中,整個計算的條件就是通過判斷這個字符串有沒有結束,如果到了最後,再通過

最開始我所說的'#'開始標誌作爲表達式的結束標誌,來計算最後的結果(這是一個特殊處理,後面會說)


/************程序實現的幾個細節***********/

我們這樣就已經把這個程序的大致框架給描繪出來了,下面來說說實現這個程序的時候需要注意到的一些細節:

(棧的基本功能的實現就飄過了~~~)

(1)首先是運算符要被push或pop時的一些比較。

這是整個程序設計中最關鍵的步驟之一,我們給這個功能命名爲Judge_Operator。有了最開始基本設計思想的講述,這段代碼應該比

較好實現了,這裏要注意到是,我並沒有把'('或着')'納入判斷中,因爲我把原括號視爲了一種特殊字符,當有圓括號出現時,就應該讓

原括號裏面的表達式獨立出來,也就是讓這個表達式單獨進入一個新的循環進行這個表達式的求值,直到原括號消失在整個算術表達

式中,具體方法是這樣的:

運算符中當是'('時,就表明其後面的必定有其他運算符(當然,不包括用戶輸入錯誤的情況,這裏不做異常處理,能力有限啊。。。)

所以,我們需要把'('push到Operator中,並且其後的一個運算符也應該被push;當遇到')'就表明有一對括號已經出現,我們就需要把這一對

括號裏的表達式計算出來,並且把'('給pop掉至於括號裏表達式的計算請看下面的代碼,由於還要考慮到括號嵌套的處理,所以我用了循環

來實現:

//'(' and ')' is a special symbol
if(expression[i] == '(')
{
	Push_Stack(&Operator,expression[i]);
}
else if(expression[i] == ')')
{
	//')' is a symbol to end a part of expression,value the result and push it to Oprand Stack
	GetElem_Stack(&Operator,&TopElemOptr);
	while(TopElemOptr != '(')
	{
		Pop_Stack(&Operator,&PopElemOptr);
		Pop_Stack(&Operand,&PopElemOpnd1);
		Pop_Stack(&Operand,&PopElemOpnd2);
		Push_Stack(&Operand,value(PopElemOpnd1,PopElemOpnd2,PopElemOptr));
		GetElem_Stack(&Operator,&TopElemOptr);
	}
	if(TopElemOptr == '(')
	{
		Pop_Stack(&Operator,&PopElemOptr);
	}
}
從代碼中,可以知道處理括號嵌套的情況是用循環解決的,每一次都要判斷棧頂元素是否是'(',我們只在if語句中考慮了')',並沒有

考慮它出現次數,因爲只要用戶輸入正確,有一個'('就必有一個')'剩下,所以只管利用'('判斷括號是否還存在於整個表達式中,當循

環完畢之後,還剩下了一個'(',我們就需要人工地給它pop掉

(2)表達式結束的判斷標識:'\0'和'#'

前面提過我將表達式存在了一個字符串中,當然得先以'\0'判斷字符串結束沒有,但是當字符串結束時,表達式的計算往往還沒有結

束,字符串的結束只是代表着表達式被算了一次,也就是把相鄰運算符的優先級相同的給合併了,但是還有不同的留在兩個棧中,我

用來作爲表達式計算開始標誌的'#'作爲表達式結束的標識,每計算一個小的表達式,就會pop一個運算符,到最後,肯定會只剩下一

開始就被push的'#',同樣利用解決括號嵌套問題的方法來解決這個問題即可。

(3)相鄰表達式的計算

這是被切成若干部分的小表達式的計算,它的關鍵就是通過Judge_Operator函數返回的每兩個相鄰運算符的優先級比較,運算符遇到

'*'或者'/'時,如果Operator的棧頂元素是'+'或者'-',就表明'*'或者'/'應該被push,如果也是'*'或者'/',也應該把它push,但是在push之前,

應該把棧頂的'/'或'*'彈出,再連續pop出兩個Operand中的元素,這三個元素被彈出來再被計算出一個新值,push到操作數棧中,如果

是'(',那麼就直接將'*'或者'/'push;如果運算符遇到'+'或者'-',很明顯,只有遇到'('時才把它push,遇到其他的運算符,都應該把前面的

表達式計算出來再把它push,我的函數實現裏是以返回'<'、'>'和'='來表示的,是一樣的。


/********************特別說明*************************/

這裏說明一下,這個程序我只實現了整數的算術表達式,源代碼同樣會放到我的代碼共享裏


/***********************Bug解決************************/

BUG1:

不能處理這種情況:開頭就是    -2+5......等的表達式

解決:

(思想:將-2的表達式變爲0.0-2即可)

只要在表達式不是數字及原括號的塊裏面首先判斷:i == 0 &&expression[i] == '-'。

如果滿足條件,就先在存放數字的棧中push一個0.0,再把'-'push到存放運算符的棧中。

BUG2:

不能處理這種情況:(-2)這種對負數有特殊要求時表達式

解決:

(思想:將(-2)變爲(0.0-2))

同樣,在上面解決BUG處的判斷語句塊後面加上專門解決這種情況的判斷語句塊。

判斷條件:expression[i] == '-' && expression[i-1]== '('

如果滿足條件,就0.0先push到存放數字的棧中,再把'-'push到運算符棧中

(解決上述兩個BUG的前提是要採用我在上面將的設計思想,就是遇到一對原括號要先把這對括號裏面的表達式給算出來再說)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章