【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()
        
    # 返回棧是否為空。棧空返回true,否則返回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()
        
    # 返回棧是否為空。棧空返回true,否則返回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
-----------------

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容