Python數(shù)據(jù)結(jié)構(gòu)與算法08:基本結(jié)構(gòu):棧的應(yīng)用之表達(dá)式轉(zhuǎn)換(下)

:本文如涉及到代碼,均經(jīng)過(guò)Python 3.7實(shí)際運(yùn)行檢驗(yàn),保證其嚴(yán)謹(jǐn)性。

本文閱讀時(shí)間約為9分鐘

接下來(lái)討論通用的中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式的算法,以及如何使用Python程序來(lái)實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式這一過(guò)程。

通用的中綴轉(zhuǎn)后綴表達(dá)式的算法思考

在后綴表達(dá)式中,操作符的出現(xiàn)順序運(yùn)算次序一致,因此會(huì)出現(xiàn)操作符調(diào)整順序的情況。

中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式的處理過(guò)程中,操作符比操作數(shù)要晚輸出。所以在程序掃描到對(duì)應(yīng)的第二個(gè)操作數(shù)之前,需要先把操作符保存起來(lái)。

這些保存起來(lái)的操作符當(dāng)中,由于優(yōu)先級(jí)的規(guī)則,有的操作符可能需要反轉(zhuǎn)次序輸出。這種保存需求和反轉(zhuǎn)特性,使得我們考慮用棧這種數(shù)據(jù)結(jié)構(gòu)來(lái)保存暫時(shí)未處理的操作符。

比如A + B * C中,"+"雖然先出現(xiàn),但是優(yōu)先級(jí)不如后面的"*",因此它要等到"*"操作符處理完成之后,才能再進(jìn)行處理。

關(guān)于操作符的優(yōu)先級(jí),由于有括號(hào)強(qiáng)制優(yōu)先級(jí)的情況,所以不一定是優(yōu)先級(jí)更高的操作符排在前面,而是要看操作符實(shí)際的優(yōu)先順序。比如(A+B)*C中,“+”的優(yōu)先級(jí)就要高于“*”,所以這個(gè)中綴表達(dá)式的后綴形式是AB+C*。對(duì)于這種情況,程序的算法又該如何解決呢?

解決方法是,程序掃描到左括號(hào)時(shí),標(biāo)記下來(lái),其后出現(xiàn)的操作符的優(yōu)先級(jí)要得到提高,當(dāng)掃描到對(duì)應(yīng)的右括號(hào)時(shí),就可以馬上輸出這個(gè)優(yōu)先級(jí)得到提高的操作符。

通用的中綴轉(zhuǎn)后綴表達(dá)式的算法總結(jié)

想通了上述的各種細(xì)節(jié),就可以對(duì)通用的中綴轉(zhuǎn)后綴表達(dá)式的算法做一個(gè)總結(jié)了:

在從左到右逐個(gè)字符掃描中綴表達(dá)式的過(guò)程中,采用一個(gè)棧來(lái)暫存未處理的操作符。

由于棧的LIFO(后進(jìn)先出)的特性,棧頂?shù)牟僮鞣褪亲罱鼤捍孢M(jìn)去的,當(dāng)遇到一個(gè)新的操作符,就需要跟棧頂?shù)牟僮鞣缺容^優(yōu)先級(jí),再?zèng)Q定如何處理。

通用的中綴轉(zhuǎn)后綴表達(dá)式的算法流程詳解

注:后面的算法描述中,約定中綴表達(dá)式是由空格隔開(kāi)的一系列單詞(token,即最小的一個(gè)詞法單位)構(gòu)成。操作符的單詞token包括*/+-(),操作數(shù)的單詞token則是單字母標(biāo)識(shí)符A、B、C等。

首先,創(chuàng)建空棧opstack用于暫存操作符,空列表postfixList用于保存后綴表達(dá)式。用字符串形式的中綴表達(dá)式轉(zhuǎn)換為單詞(token)作為元素構(gòu)成的列表。

例如,"A + B * C"字符串利用字符串方法string.split(" ")轉(zhuǎn)換成由操作數(shù)和操作符構(gòu)成的列表['A', '+', 'B', '*', 'C']。
然后,再?gòu)淖笾劣覓呙柚芯Y表達(dá)式單詞列表,會(huì)有以下幾種情況:

  • 如果單詞token是操作數(shù),則直接添加到后綴表達(dá)式列表postfixList的末尾;
  • 如果單詞token是左括號(hào)“(”,則壓入opstack棧頂(所謂壓入棧頂,就是將元素入棧);
  • 如果單詞token是右括號(hào)“)”,則反復(fù)彈出opstack棧頂操作符(所謂彈出棧頂,就是將元素移除出棧),加入到輸出列表postfixList的末尾,直到碰到左括號(hào)為止;
  • 如果單詞是操作符“*/+-”,則壓入opstack棧頂——壓入之前,要比較其與opstack棧當(dāng)前的棧頂操作符的優(yōu)先級(jí)。如果當(dāng)前棧頂?shù)牟僮鞣膬?yōu)先級(jí)高于或等于它,則要反復(fù)彈出棧頂?shù)牟僮鞣瑢m數(shù)牟僮鞣敵霾⒓尤氲搅斜韕ostfixList的末尾,直到opstack棧頂?shù)牟僮鞣膬?yōu)先級(jí)低于它為止。把只裝操作符的棧想象成一堆盤(pán)子,上面的是棧頂,下面的是棧底,這里的要求是,棧底的操作符的優(yōu)先級(jí)必須小于棧頂?shù)牟僮鞣?/li>

接著,中綴表達(dá)式單詞列表掃描結(jié)束后,把opstack棧中的所有剩余操作符依次彈出,添加到輸出列表postfixList的末尾。

最后,把輸出列表postfixList再用join方法使其轉(zhuǎn)化為字符串形式的后綴表達(dá)式,算法結(jié)束。

具體實(shí)現(xiàn)的代碼如下:

# 中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式的程序。
class Stack:
    def __init__(self):
        self.items = []
        
    def isEmpty(self):
        return self.items == []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def peek(self):
        return self.items[len(self.items)-1]

    def size(self):
        return len(self.items)

def infixToPostfix(infixexpr):
    """
    infixexpre:一個(gè)字符串類型的中綴表達(dá)式,操作數(shù)與操作符之間都由空格" "隔開(kāi)。
    如"A+B*C+D"是錯(cuò)誤的輸入,"A + B * C + D"才是正確的輸入。
    """    
    # 下面連續(xù)6行是用一個(gè)字典記錄操作符優(yōu)先級(jí)。
    prec = {}
    prec["*"] = 3
    prec["/"] = 3
    prec["+"] = 2
    prec["-"] = 2
    prec["("] = 1
    opStack = Stack()  # 創(chuàng)建一個(gè)名為opStack的空棧。
    postfixList = []
    tokenList = infixexpr.split()  # 解析表達(dá)式到單詞列表。
    for token in tokenList:
        if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":  #操作數(shù)。
            postfixList.append(token)
        elif token == '(':  # 碰到左括號(hào)“(”,直接輸出。
            opStack.push(token)
        elif token == ')':  # 碰到右括號(hào)“)”,不停地彈出來(lái)。
            topToken = opStack.pop()
            while topToken != '(':
                postfixList.append(topToken)
                topToken = opStack.pop()
        else:  # 碰到操作符也是不停地彈出,直到棧頂?shù)牟僮鞣麅?yōu)先級(jí)比自己低。
            while (not opStack.isEmpty()) and \
            (prec[opStack.peek()] >= prec[token]):
                postfixList.append(opStack.pop())
            opStack.push(token)
    while not opStack.isEmpty():
            postfixList.append(opStack.pop())  # 操作符。
    return " ".join(postfixList)  # 合成后綴表達(dá)式字符串。 
        
print(infixToPostfix("A + B * C + D"))  
<<<A B C * + D +

To be continued.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容