【Python】(十)后綴表達式

python實現棧的代碼回顧

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

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

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

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

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

    # 返回棧的長度,即棧中元素的個數。
    def size(self):
        return len(self.items)

后綴表達式回顧

后綴表達式是計算機科學中的一種常見的數學表達式形式。相比于人類常用的中綴表達,后綴表達式在沒有括號的情況下也不會引起運算順序上的歧義,這是其最重要的優點。

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

中綴表達式轉換為后綴表達式

假設表達式中只有大寫字母、數字和運算符,如何將一個中綴表達式轉換為后綴表達式?
解決這一問題的要點有如下:

  • 大寫字母和數字的排列順序,中綴表達式和后綴表達式是相同的。
  • 優先級更高的運算符和靠左的同級別運算符應當更早在后綴表達式中出現。
  • 括號相當于更高的一個平臺,左括號作為平臺的開始,右括號作為平臺的結束。
class Stack(object):
    # Stack() 創建一個空的新棧。
    def __init__(self):
        self.items = []
    
    # push(new_item)將一個新的元素添加到棧頂。
    def push(self, new_item):
        self.items.append(new_item)
    
    # 返回棧頂元素,但不會彈出它。
    def top(self):
        return self.items[-1]
        
    # 彈出并返回棧頂元素
    def pop(self):
        return self.items.pop()
        
    # 返回棧是否為空。??辗祷豻rue,否則返回false。
    def isEmpty(self):
        return [] == self.items
    
    # 返回棧的長度,即棧中元素的個數。
    def size(self):
        return len(self.items)
    
# 將中綴表達式變換成后綴表達式
# trans_string為傳入的待轉換的中綴表達式
# op_rank為運算符的優先級,數字越大,優先級越高
def mid2post(trans_string, op_rank):
    post_list = [] #創建一個空列表,用于保存生成中的后綴表達式
    stack = Stack() #創建一個新的棧用于保存未被輸出的運算符
    
    for checking_char in trans_string: #從左到右,逐個遍歷中綴表達式中的字符
        if checking_char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789': #當前字符為字母或數字
            post_list.append(checking_char) #將字符接入后綴表達式
        elif checking_char == '(': #當前符號為左括號
            stack.push(checking_char) #將左括號入棧,代表著后面出現的運算符優先級更高
        elif checking_char == ')': #當前符號為右括號
            top_char = stack.pop() 
            while top_char != '(': #彈出元素,接入后綴表達式,直到出現配對的左括號
                post_list.append(top_char)
                top_char = stack.pop()
        else: #符號為運算符
            while (not stack.isEmpty()) and (op_rank[stack.top()]>=op_rank[checking_char]): #棧不為空,且棧頂符號優先級不低于當前符號
                post_list.append(stack.pop()) #彈出這個更靠前的運算符,并接入后綴表達式
            stack.push(checking_char) #將運算符壓入棧
    
    while not stack.isEmpty(): #當棧未空
        post_list.append(stack.pop()) #彈出所有運算符,并接入后綴表達式
    
    return ''.join(post_list) #將列表轉換為字符串并返回
        
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()

運行結果如下:

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+
-----------------

計算后綴表達式

如何高效的計算后綴表達式?
這一問題同樣可以使用棧來解決,而且相比之前的轉換問題更加簡單。
解決這一問題的要點有如下:

  • 每當在輸入上看到運算符時,計算兩個最近的操作數。
  • 操作數的先后次序不能變。

我們以最簡單的個位數運算為例,編寫代碼:

class Stack(object):
    # Stack() 創建一個空的新棧。
    def __init__(self):
        self.items = []
    
    # push(new_item)將一個新的元素添加到棧頂。
    def push(self, new_item):
        self.items.append(new_item)
    
    # 返回棧頂元素,但不會彈出它。
    def top(self):
        return self.items[-1]
        
    # 彈出并返回棧頂元素
    def pop(self):
        return self.items.pop()
        
    # 返回棧是否為空。??辗祷豻rue,否則返回false。
    def isEmpty(self):
        return [] == self.items
    
    # 返回棧的長度,即棧中元素的個數。
    def size(self):
        return len(self.items)
    
# 將中綴表達式變換成后綴表達式
# trans_string為傳入的待轉換的中綴表達式
# op_rank為運算符的優先級,數字越大,優先級越高
def mid2post(trans_string, op_rank):
    post_list = [] #創建一個空列表,用于保存生成中的后綴表達式
    stack = Stack() #創建一個新的棧用于保存未被輸出的運算符
    
    for checking_char in trans_string: #從左到右,逐個遍歷中綴表達式中的字符
        if checking_char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789': #當前字符為字母或數字
            post_list.append(checking_char) #將字符接入后綴表達式
        elif checking_char == '(': #當前符號為左括號
            stack.push(checking_char) #將左括號入棧,代表著后面出現的運算符優先級更高
        elif checking_char == ')': #當前符號為右括號
            top_char = stack.pop() 
            while top_char != '(': #彈出元素,接入后綴表達式,直到出現配對的左括號
                post_list.append(top_char)
                top_char = stack.pop()
        else: #符號為運算符
            while (not stack.isEmpty()) and (op_rank[stack.top()]>=op_rank[checking_char]): #棧不為空,且棧頂符號優先級不低于當前符號
                post_list.append(stack.pop()) #彈出這個更靠前的運算符,并接入后綴表達式
            stack.push(checking_char) #將運算符壓入棧
    
    while not stack.isEmpty(): #當棧未空
        post_list.append(stack.pop()) #彈出所有運算符,并接入后綴表達式
    
    return ''.join(post_list) #將列表轉換為字符串并返回
    
# 將計算后綴表達式
# post_string為傳入的待計算的后綴表達式
# op_rank為運算符的優先級,數字越大,優先級越高
def compute_post(post_string):
    stack = Stack() #創建一個新的棧用于保存未被輸出的運算符
    
    for computing_char in post_string: #從左遍歷
        if computing_char in '01234567889': #如果當前為數字
            stack.push(computing_char) #壓入棧
        else: #如果為運算符
            value_2 = int(stack.pop()) #先彈出后一個操作數
            value_1 = int(stack.pop()) #后彈出前一個操作數
            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) #將運算結果入棧
            
    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()

運行結果為:

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
-----------------

結果正確。如果需要計算兩位數以上的操作數,則需要利用空格等分隔符來對操作數進行分隔。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容