数据结构-栈--表达式
數(shù)據(jù)結(jié)構(gòu)之棧在表達(dá)式求值及轉(zhuǎn)換中的應(yīng)用
- 1 棧及表達(dá)式的基本定義
- 2 棧在表達(dá)式求值過程以及轉(zhuǎn)換中的應(yīng)用
1 棧及表達(dá)式的基本定義
1.1棧的定義
棧(stack)是限定僅在表尾進(jìn)行插入或刪除操作的線性表。因此,對(duì)棧來說,
表尾端有其特殊含義,稱為棧頂(top),相應(yīng)地,表頭端稱為棧底(bottom)。不含元素的空表稱為空棧。棧也稱為后進(jìn)先出表。下面將給出關(guān)于棧基本操作的相關(guān)函數(shù):
InitStack(&S):構(gòu)造一個(gè)空棧S
GetTop(S,&e):用e返回S的棧頂元素。
Push(&S,e):插人元素e為新的棧頂元素。
Pop(&s,&e):刪除S的棧頂元素,并用e返回其值。
Precede(m,n):運(yùn)算符的優(yōu)先級(jí)比較函數(shù)。
1.2表達(dá)式的定義
任何一個(gè)表達(dá)式都是由操作數(shù)(operand)、運(yùn)算符(operator)和界限符(elimite)組成的,我們稱它們?yōu)閱卧~。一般地,操作數(shù)既可以是常數(shù)也可以是被說明為變量或常量的標(biāo)識(shí)符;運(yùn)算符可以分為算術(shù)運(yùn)算符、關(guān)系運(yùn)算符和邏輯運(yùn)算符3類;基本界限符有左右括號(hào)和表達(dá)式結(jié)束符等。
1.3表達(dá)式的分類
表達(dá)式一般分為前綴表達(dá)式,中綴表達(dá)式和后綴表達(dá)式,在計(jì)算機(jī)中,計(jì)算后綴表達(dá)式值的時(shí)間空間消耗往往比計(jì)算中綴表達(dá)式的消耗少,因此我們通常將普通表達(dá)式轉(zhuǎn)化為某種類型表達(dá)式來達(dá)到減少?gòu)?fù)雜度的目的。
1.3.1 前綴表達(dá)式 前綴表達(dá)式是將運(yùn)算符寫在前面,操作數(shù)寫在后面。為紀(jì)念其發(fā)明者波蘭數(shù)學(xué)家Jan Lukasiewicz,前綴表達(dá)式也稱為“波蘭式”。
1.3.2 中綴表達(dá)式 中綴表達(dá)式是指運(yùn)算符在兩個(gè)操作數(shù)的中間。即平時(shí)常用的表達(dá)式脫括號(hào)后的形式。
1.3.3.后綴表達(dá)式 后綴表達(dá)式是將運(yùn)算符放在兩個(gè)運(yùn)算對(duì)象的后面,計(jì)算時(shí)按運(yùn)算符出現(xiàn)的順序,嚴(yán)格從左向右進(jìn)行(不再考慮運(yùn)算符的優(yōu)先規(guī)則)。運(yùn)用次數(shù)最多,又稱“逆波蘭式”。
對(duì)于算術(shù)表達(dá)式Exp=ab+(c-d/e)f,它的三種表達(dá)形式為:前綴式:+ab-c/def,中綴式:ab+c-d/ef,后綴式:abcde/-f+。
1.4關(guān)于上述表達(dá)式的相關(guān)結(jié)論
1.操作數(shù)之間的相對(duì)次序不變;
2.運(yùn)算符的相對(duì)次序不同;
3.中綴式丟失了括弧信息,致使運(yùn)算的次序不確定;
4.前綴式的運(yùn)算規(guī)則為:連續(xù)出現(xiàn)的兩個(gè)操作數(shù)和在它們之前且緊靠它們的運(yùn)算符構(gòu)成一個(gè)最小表達(dá)式;
5.后綴式的運(yùn)算規(guī)則為:運(yùn)算符在式中出現(xiàn)的順序恰為表達(dá)式的運(yùn)算順序;每個(gè)運(yùn)算符和在它之前出現(xiàn)且緊靠它的兩個(gè)操作數(shù)構(gòu)成一個(gè)最小表達(dá)式。
2 棧在表達(dá)式求值過程以及轉(zhuǎn)換中的應(yīng)用
2.1 表達(dá)式求值算法的基本思路
為了敘述的簡(jiǎn)潔,我們僅討論簡(jiǎn)單算術(shù)表達(dá)式的求值問題。這種表達(dá)式只含加、減、乘、除4種運(yùn)算符。讀者不難將它推廣到更一般的表達(dá)式上。
2.1.1 求值算法中的主要數(shù)據(jù)結(jié)構(gòu) 我們分別用順序棧來寄存表達(dá)式的操作數(shù)和運(yùn)算符。為了實(shí)現(xiàn)算符優(yōu)先算法,可以使用兩個(gè)工作棧。一個(gè)稱做OPTR,用以寄存運(yùn)算符;另一個(gè)稱做OPND,用以寄存操作數(shù)或運(yùn)算結(jié)果。
2.1.2 關(guān)于表達(dá)式的分析與判斷 表達(dá)式求值程序的關(guān)鍵是對(duì)數(shù)字與運(yùn)算符的判斷和運(yùn)算符優(yōu)先級(jí)的判斷,以及出棧的運(yùn)算。可以建立兩個(gè)棧,分別存儲(chǔ)數(shù)字與運(yùn)算符,棧OPTR存運(yùn)算符,棧OPND存數(shù)字。依次讀取表達(dá)式的字符串。讀取字符的時(shí)候需要判斷字符的類型,先判斷是數(shù)字還是運(yùn)算符:
第一步,如果是數(shù)字,把數(shù)字壓入棧OPND。
第二步,如果是運(yùn)算符,那么在壓入運(yùn)算符之前,先將要壓入的與棧頂?shù)倪\(yùn)算符優(yōu)先級(jí)相比較,此時(shí)有三種情況:
1.如果當(dāng)前的運(yùn)算符優(yōu)先級(jí)等于棧頂,則脫括號(hào)并接受下一字符;
2.若當(dāng)前的運(yùn)算符優(yōu)先級(jí)大于棧頂?shù)?#xff0c;則壓入;
3.若當(dāng)前的運(yùn)算符優(yōu)先級(jí)小于棧內(nèi)時(shí),彈出棧頂?shù)倪\(yùn)算符,同時(shí)彈出兩組數(shù)字,經(jīng)過運(yùn)算符的運(yùn)算后再重新壓到棧內(nèi)。
為了方便判斷運(yùn)算結(jié)束,在存儲(chǔ)運(yùn)算符之前先將“#”壓入棧OPTR中,在輸入表達(dá)式時(shí)以“#”結(jié)束,所以當(dāng)接受的運(yùn)算符為‘#’并且OPTR棧頂?shù)脑匾矠椤?’來作為運(yùn)算結(jié)束的條件,經(jīng)過上述的討論,最后棧OPND的數(shù)值,即為表達(dá)式求值的最終結(jié)果。
2.1.3 表達(dá)式求值例題分析
例1:利用上述算法對(duì)算數(shù)表達(dá)式3*(7-2)求值
下圖中,左側(cè)為OPTR棧,右側(cè)為OPND棧。
步驟1:“#”入OPTR棧
步驟2:“3”入OPND棧
步驟3:“”比此時(shí)的OPTR棧的頂元素“#”的優(yōu)先權(quán)高,“”入OPTR棧
步驟4.:“(”比此時(shí)的OPTR棧頂元素“*”的優(yōu)先權(quán)高,“(”入OPTR棧
步驟5:“7”是數(shù)字,進(jìn)OPND棧
步驟6.:“-”比此時(shí)的OPTR棧頂元素“(”的優(yōu)先權(quán)高,“-”入OPTR棧
步驟7.:“2”入OPND棧
步驟8.:“)”比此時(shí)的OPTR棧頂元素“-”的優(yōu)先權(quán)低,執(zhí)行Pop(OPTR,theta);操作,“-”退OPTR棧,OPND棧中退出兩個(gè)數(shù),在該步操作中執(zhí)行“7-2”,把得到的結(jié)果5壓入OPND棧中,此時(shí)兩個(gè)棧的元素為下圖:
步驟9:“)”繼續(xù)和OPTR棧此時(shí)的棧頂元素“(”相比,發(fā)現(xiàn)相等,則脫括號(hào),并接受下一字符,此時(shí)的OPND棧為:
步驟10:我們通常以“#”來作為表達(dá)式的末尾,所以最后需要判斷優(yōu)先級(jí)大小的是“#”和OPTR的棧頂元素“”,此時(shí)發(fā)現(xiàn)棧頂元素“”的優(yōu)先級(jí)高,則棧頂元素“”出棧,OPND棧中出兩個(gè)元素,執(zhí)行“35”操作,得到15,壓入OPND棧內(nèi),最后兩個(gè)“#”相等,while循環(huán)結(jié)束。則OPND棧中的元素就是該表達(dá)式的最終求值。此時(shí),兩個(gè)棧的元素為:
最后結(jié)果為3*(7-2)=15。
2.1.4 算法時(shí)間和空間性能分析
1.時(shí)間復(fù)雜度:對(duì)于含n個(gè)字符的表達(dá)式,無論是對(duì)其進(jìn)行合法性檢測(cè)還是對(duì)其進(jìn)行入棧出棧操作n次,因此其時(shí)間復(fù)雜度為0(n)。
2.空間復(fù)雜度:由于在本程序中,在為算符棧(OPTR)和操作數(shù)棧(0PND)涉及到兩種情況時(shí)申請(qǐng)空間,一方面分別為OPTR棧和0PND棧申請(qǐng)了初始的存儲(chǔ)單元,均為STACKINITSIZE=100個(gè);另一方面,考慮到兩個(gè)棧在處理具體的算術(shù)表達(dá)式時(shí),有可能會(huì)出現(xiàn)溢出的情況,即棧的初始的存儲(chǔ)空間不夠用,這時(shí)需要為其申請(qǐng)額外的存儲(chǔ)空間,每溢出一次,就為其申請(qǐng)存儲(chǔ)單元STACKINCREMENT=10個(gè),所以,本程序的算法的空間復(fù)雜度一方面取決于算術(shù)表達(dá)式的長(zhǎng)度,另一方面取決于本程序的所有代碼所占用的存儲(chǔ)空間大小。
2.2 后綴表達(dá)式求值算法
2.2.1求值算法中的主要數(shù)據(jù)結(jié)構(gòu) 根本思想就是先找運(yùn)算符,再找操作數(shù),因?yàn)椴僮鲾?shù)來的早晚與運(yùn)算順序無關(guān),所以,我們可以設(shè)置一個(gè)棧來存儲(chǔ)操作數(shù),只要是操作數(shù)據(jù)進(jìn)棧,只要是運(yùn)算符就從棧中取出兩個(gè)操作數(shù),經(jīng)過運(yùn)算符的運(yùn)算后再重新壓到棧內(nèi),最后棧頂?shù)闹稻褪俏覀円蟮淖詈蠼Y(jié)果。
2.2.2基于C語(yǔ)言的算法
下面給出其基本算法:
int GetValue NiBoLan(char *str)/對(duì)逆波蘭式求值
{ p=str;InitStack(s); //s為操作數(shù)棧
while(*p!="#’)
{ if(Isdigit(*p)) push(s,*p);
else {
pop(s,a);pop(s,b);
r=compute(b,*p,a); //假設(shè)compute為執(zhí)行雙目運(yùn)算的過程
push(s,r);}//else
}//while
}//GetValue_ NiBoLan
2.3 原表達(dá)式求后綴表達(dá)式算法
2.3.1 原表達(dá)式求后綴式算法思想 因?yàn)檫\(yùn)算符來的早晚與運(yùn)算順序無關(guān),首先設(shè)立暫存運(yùn)算符的棧,設(shè)表達(dá)式的結(jié)束符號(hào)為“#”,預(yù)設(shè)運(yùn)算符棧的棧底為“#”,若當(dāng)前字符是操作數(shù),則直接發(fā)送給后綴式;若當(dāng)前運(yùn)算符的優(yōu)先數(shù)高于棧頂運(yùn)算符,則進(jìn)棧;否則,退出棧頂運(yùn)算行發(fā)送給后綴式;“(”對(duì)它之前后的運(yùn)算符起隔離作用,“)”可視為自相應(yīng)左括弧開始的表達(dá)式的結(jié)束符。
2.3.2 原表達(dá)式求后綴式例題分析
對(duì)原表達(dá)式2*(9+6/3-5)+4求其后綴式:
最后求得后綴表達(dá)式為:2963/+5-*4+。
參考文獻(xiàn):
[1] 嚴(yán)蔚敏,吳偉民.數(shù)據(jù)結(jié)構(gòu)C語(yǔ)言版[M].北京:清華大學(xué)出版社。
這是自己總結(jié)的表達(dá)式求值的基本算法,也是自己的寫的一篇很簡(jiǎn)單的課程論文,主要是給出了相應(yīng)的例題分析,圖片是自己做的,不是很美觀,有錯(cuò)誤歡迎大家評(píng)論區(qū)指出討論,大家一起進(jìn)步!
總結(jié)
以上是生活随笔為你收集整理的数据结构-栈--表达式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快速排序原理和实现(图文讲解)
- 下一篇: GAAS使用的硬件配置