【Python】(十)后綴表達(dá)式

python實(shí)現(xiàn)棧的代碼回顧

class Stack(object):
    # Stack() 創(chuàng)建一個(gè)空的新棧。
    def __init__(self):
        self.items = []

    # push(new_item)將一個(gè)新的元素添加到棧頂阎肝。
    def push(self, new_item):
        self.items.append(new_item)

    # 返回棧頂元素锦针,但不會(huì)彈出它。
    def top(self):
        return self.items[-1]

    # 彈出并返回棧頂元素
    def pop(self):
        return self.items.pop()

    # 返回棧是否為空。椂遥空返回true,否則返回false亚隅。
    def isEmpty(self):
        return [] == self.items

    # 返回棧的長(zhǎng)度,即棧中元素的個(gè)數(shù)狮崩。
    def size(self):
        return len(self.items)

后綴表達(dá)式回顧

后綴表達(dá)式是計(jì)算機(jī)科學(xué)中的一種常見(jiàn)的數(shù)學(xué)表達(dá)式形式。相比于人類(lèi)常用的中綴表達(dá)鹿寻,后綴表達(dá)式在沒(méi)有括號(hào)的情況下也不會(huì)引起運(yùn)算順序上的歧義,這是其最重要的優(yōu)點(diǎn)诽凌。

中綴表達(dá)式 → 后綴表達(dá)式
A+B → AB+
A+BC → ABC+
(A+B)C → AB+C
(A+B)(C+D) → AB+CD+
A+B+C+D → AB+C+D+

中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式

假設(shè)表達(dá)式中只有大寫(xiě)字母毡熏、數(shù)字和運(yùn)算符,如何將一個(gè)中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式侣诵?
解決這一問(wèn)題的要點(diǎn)有如下:

  • 大寫(xiě)字母和數(shù)字的排列順序痢法,中綴表達(dá)式和后綴表達(dá)式是相同的。
  • 優(yōu)先級(jí)更高的運(yùn)算符和靠左的同級(jí)別運(yùn)算符應(yīng)當(dāng)更早在后綴表達(dá)式中出現(xiàn)杜顺。
  • 括號(hào)相當(dāng)于更高的一個(gè)平臺(tái)财搁,左括號(hào)作為平臺(tái)的開(kāi)始,右括號(hào)作為平臺(tái)的結(jié)束躬络。
class Stack(object):
    # Stack() 創(chuàng)建一個(gè)空的新棧尖奔。
    def __init__(self):
        self.items = []
    
    # push(new_item)將一個(gè)新的元素添加到棧頂。
    def push(self, new_item):
        self.items.append(new_item)
    
    # 返回棧頂元素穷当,但不會(huì)彈出它提茁。
    def top(self):
        return self.items[-1]
        
    # 彈出并返回棧頂元素
    def pop(self):
        return self.items.pop()
        
    # 返回棧是否為空。椖俨耍空返回true茴扁,否則返回false。
    def isEmpty(self):
        return [] == self.items
    
    # 返回棧的長(zhǎng)度汪疮,即棧中元素的個(gè)數(shù)峭火。
    def size(self):
        return len(self.items)
    
# 將中綴表達(dá)式變換成后綴表達(dá)式
# trans_string為傳入的待轉(zhuǎn)換的中綴表達(dá)式
# op_rank為運(yùn)算符的優(yōu)先級(jí)毁习,數(shù)字越大,優(yōu)先級(jí)越高
def mid2post(trans_string, op_rank):
    post_list = [] #創(chuàng)建一個(gè)空列表卖丸,用于保存生成中的后綴表達(dá)式
    stack = Stack() #創(chuàng)建一個(gè)新的棧用于保存未被輸出的運(yùn)算符
    
    for checking_char in trans_string: #從左到右蜓洪,逐個(gè)遍歷中綴表達(dá)式中的字符
        if checking_char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789': #當(dāng)前字符為字母或數(shù)字
            post_list.append(checking_char) #將字符接入后綴表達(dá)式
        elif checking_char == '(': #當(dāng)前符號(hào)為左括號(hào)
            stack.push(checking_char) #將左括號(hào)入棧,代表著后面出現(xiàn)的運(yùn)算符優(yōu)先級(jí)更高
        elif checking_char == ')': #當(dāng)前符號(hào)為右括號(hào)
            top_char = stack.pop() 
            while top_char != '(': #彈出元素坯苹,接入后綴表達(dá)式隆檀,直到出現(xiàn)配對(duì)的左括號(hào)
                post_list.append(top_char)
                top_char = stack.pop()
        else: #符號(hào)為運(yùn)算符
            while (not stack.isEmpty()) and (op_rank[stack.top()]>=op_rank[checking_char]): #棧不為空,且棧頂符號(hào)優(yōu)先級(jí)不低于當(dāng)前符號(hào)
                post_list.append(stack.pop()) #彈出這個(gè)更靠前的運(yùn)算符粹湃,并接入后綴表達(dá)式
            stack.push(checking_char) #將運(yùn)算符壓入棧
    
    while not stack.isEmpty(): #當(dāng)棧未空
        post_list.append(stack.pop()) #彈出所有運(yùn)算符恐仑,并接入后綴表達(dá)式
    
    return ''.join(post_list) #將列表轉(zhuǎn)換為字符串并返回
        
def main():
    op_rank = {'*':2, '/':2, '+':1, '-':1, '(':0}
    string_list = ['A+B', 'A+B*C', '(A+B)*C', '(A+B)*(C+D)', 'A+B+C+D']
    for trans_string in string_list:
        print("%s --> %s"%( trans_string, mid2post(trans_string,op_rank)))
        print("-----------------")

if __name__ == "__main__":
    main()

運(yùn)行結(jié)果如下:

A+B --> AB+
-----------------
A+B*C --> ABC*+
-----------------
(A+B)*C --> AB+C*
-----------------
(A+B)*(C+D) --> AB+CD+*
-----------------
A+B+C+D --> AB+C+D+
-----------------

計(jì)算后綴表達(dá)式

如何高效的計(jì)算后綴表達(dá)式?
這一問(wèn)題同樣可以使用棧來(lái)解決为鳄,而且相比之前的轉(zhuǎn)換問(wèn)題更加簡(jiǎn)單裳仆。
解決這一問(wèn)題的要點(diǎn)有如下:

  • 每當(dāng)在輸入上看到運(yùn)算符時(shí),計(jì)算兩個(gè)最近的操作數(shù)孤钦。
  • 操作數(shù)的先后次序不能變歧斟。

我們以最簡(jiǎn)單的個(gè)位數(shù)運(yùn)算為例,編寫(xiě)代碼:

class Stack(object):
    # Stack() 創(chuàng)建一個(gè)空的新棧偏形。
    def __init__(self):
        self.items = []
    
    # push(new_item)將一個(gè)新的元素添加到棧頂静袖。
    def push(self, new_item):
        self.items.append(new_item)
    
    # 返回棧頂元素,但不會(huì)彈出它俊扭。
    def top(self):
        return self.items[-1]
        
    # 彈出并返回棧頂元素
    def pop(self):
        return self.items.pop()
        
    # 返回棧是否為空队橙。棧空返回true萨惑,否則返回false捐康。
    def isEmpty(self):
        return [] == self.items
    
    # 返回棧的長(zhǎng)度,即棧中元素的個(gè)數(shù)庸蔼。
    def size(self):
        return len(self.items)
    
# 將中綴表達(dá)式變換成后綴表達(dá)式
# trans_string為傳入的待轉(zhuǎn)換的中綴表達(dá)式
# op_rank為運(yùn)算符的優(yōu)先級(jí)解总,數(shù)字越大,優(yōu)先級(jí)越高
def mid2post(trans_string, op_rank):
    post_list = [] #創(chuàng)建一個(gè)空列表姐仅,用于保存生成中的后綴表達(dá)式
    stack = Stack() #創(chuàng)建一個(gè)新的棧用于保存未被輸出的運(yùn)算符
    
    for checking_char in trans_string: #從左到右花枫,逐個(gè)遍歷中綴表達(dá)式中的字符
        if checking_char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789': #當(dāng)前字符為字母或數(shù)字
            post_list.append(checking_char) #將字符接入后綴表達(dá)式
        elif checking_char == '(': #當(dāng)前符號(hào)為左括號(hào)
            stack.push(checking_char) #將左括號(hào)入棧,代表著后面出現(xiàn)的運(yùn)算符優(yōu)先級(jí)更高
        elif checking_char == ')': #當(dāng)前符號(hào)為右括號(hào)
            top_char = stack.pop() 
            while top_char != '(': #彈出元素萍嬉,接入后綴表達(dá)式乌昔,直到出現(xiàn)配對(duì)的左括號(hào)
                post_list.append(top_char)
                top_char = stack.pop()
        else: #符號(hào)為運(yùn)算符
            while (not stack.isEmpty()) and (op_rank[stack.top()]>=op_rank[checking_char]): #棧不為空,且棧頂符號(hào)優(yōu)先級(jí)不低于當(dāng)前符號(hào)
                post_list.append(stack.pop()) #彈出這個(gè)更靠前的運(yùn)算符壤追,并接入后綴表達(dá)式
            stack.push(checking_char) #將運(yùn)算符壓入棧
    
    while not stack.isEmpty(): #當(dāng)棧未空
        post_list.append(stack.pop()) #彈出所有運(yùn)算符磕道,并接入后綴表達(dá)式
    
    return ''.join(post_list) #將列表轉(zhuǎn)換為字符串并返回
    
# 將計(jì)算后綴表達(dá)式
# post_string為傳入的待計(jì)算的后綴表達(dá)式
# op_rank為運(yùn)算符的優(yōu)先級(jí),數(shù)字越大行冰,優(yōu)先級(jí)越高
def compute_post(post_string):
    stack = Stack() #創(chuàng)建一個(gè)新的棧用于保存未被輸出的運(yùn)算符
    
    for computing_char in post_string: #從左遍歷
        if computing_char in '01234567889': #如果當(dāng)前為數(shù)字
            stack.push(computing_char) #壓入棧
        else: #如果為運(yùn)算符
            value_2 = int(stack.pop()) #先彈出后一個(gè)操作數(shù)
            value_1 = int(stack.pop()) #后彈出前一個(gè)操作數(shù)
            if computing_char == '+':
                value_3 = value_1 + value_2
            elif computing_char == '-':
                value_3 = value_1 - value_2
            elif computing_char == '*':
                value_3 = value_1 * value_2
            else:
                value_3 = value_1 / value_2
            stack.push(value_3) #將運(yùn)算結(jié)果入棧
            
    return stack.pop()
        
def main():
    op_rank = {'*':2, '/':2, '+':1, '-':1, '(':0}
    string_list = ['1+2', '1+2*3', '(1+2)*3', '(1+2)*(3+4)', '1+2+3+4']
    for trans_string in string_list:
        print("%s --> %s --> %d" % (trans_string, mid2post(trans_string,op_rank), compute_post(mid2post(trans_string,op_rank)) ))
        print("-----------------")

if __name__ == "__main__":
    main()

運(yùn)行結(jié)果為:

1+2 --> 12+ --> 3
-----------------
1+2*3 --> 123*+ --> 7
-----------------
(1+2)*3 --> 12+3* --> 9
-----------------
(1+2)*(3+4) --> 12+34+* --> 21
-----------------
1+2+3+4 --> 12+3+4+ --> 10
-----------------

結(jié)果正確溺蕉。如果需要計(jì)算兩位數(shù)以上的操作數(shù)伶丐,則需要利用空格等分隔符來(lái)對(duì)操作數(shù)進(jìn)行分隔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疯特,一起剝皮案震驚了整個(gè)濱河市哗魂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漓雅,老刑警劉巖录别,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異邻吞,居然都是意外死亡组题,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)抱冷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)崔列,“玉大人,你說(shuō)我怎么就攤上這事旺遮≌匝叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵耿眉,是天一觀的道長(zhǎng)边翼。 經(jīng)常有香客問(wèn)我,道長(zhǎng)跷敬,這世上最難降的妖魔是什么讯私? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮西傀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桶癣。我一直安慰自己拥褂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布牙寞。 她就那樣靜靜地躺著饺鹃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪间雀。 梳的紋絲不亂的頭發(fā)上悔详,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音惹挟,去河邊找鬼茄螃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛连锯,可吹牛的內(nèi)容都是我干的归苍。 我是一名探鬼主播用狱,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拼弃!你這毒婦竟也來(lái)了夏伊?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吻氧,失蹤者是張志新(化名)和其女友劉穎溺忧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盯孙,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鲁森,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镀梭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刀森。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖报账,靈堂內(nèi)的尸體忽然破棺而出研底,到底是詐尸還是另有隱情,我是刑警寧澤透罢,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布榜晦,位于F島的核電站,受9級(jí)特大地震影響羽圃,放射性物質(zhì)發(fā)生泄漏乾胶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一朽寞、第九天 我趴在偏房一處隱蔽的房頂上張望识窿。 院中可真熱鬧,春花似錦脑融、人聲如沸喻频。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甥温。三九已至,卻和暖如春妓布,著一層夾襖步出監(jiān)牢的瞬間姻蚓,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工匣沼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狰挡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像圆兵,于是被迫代替她去往敵國(guó)和親跺讯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容