1. 程式人生 > 實用技巧 >棧和佇列(二):棧的經典問題

棧和佇列(二):棧的經典問題

引言

本節主要介紹棧的經典應用:

  • 中綴表示式轉字尾表示式
  • 字尾表示式的計算
  • 括號問題

中綴表示式與字尾表示式

首先來介紹一下這兩種表示式:

  • 中綴表示式:這名字看起來很專業,實際上它就是我們日常打草稿時所用的計算表示式,如'1+2×3/4+(2-1)='這樣的式子就是中綴表示式;但是中綴表示式不足以表示所有可能的運算順序。
  • 字尾表示式:所有運算子都在運算物件之後,不需要引進括號,適合計算機處理;由於字首表示式被稱為波蘭表示式,因此後綴表示式也被稱為逆波蘭表示式。

那麼如何來進行表示式之間的轉換呢?

  1. 如果遇到運算物件,我們可以直接將其擴充套件到列表中;

  2. 如果遇到左括號或者運算子棧為空時,我們直接將其壓入棧;

  3. 如果遇到運算子號,那麼就比較棧頂符號和當前符號的優先順序,當棧頂符號優先順序高時則必須將其輸出,這樣操作符合運算邏輯;

  4. 只有遇到右括號時,左括號才可以彈出,雖然括號優先順序高,但是為了去掉括號,必須這麼做;

Python實現:

這裡為了簡化程式碼,不再專門為中綴表示式建立生成器,輸入的表示式引數均為有效的。

def infix_to_postfix(s):
    priority = {'(':1, '+':2, '-':2, '*':3, '/':3}
    signal = '()-+*/'
    tmp = [] # 運算子快取空間
    exp = [] # 表示式
    for x in s:
        
        if x not in signal:
            exp.append(x)
        elif len(tmp) == 0 or x == '(':
            tmp.append(x)
        elif x == ')':
            while len(tmp) > 0 and tmp[-1] != '(':
                exp.append(tmp.pop())
            if len(tmp) == 0:
                raise SyntaxError('不存在對應的"("')
            tmp.pop()
        else:
            while len(tmp) > 0 and priority[tmp[-1]] >= priority[x]:
                exp.append(tmp.pop())
            tmp.append(x)
    while len(tmp) > 0:
        if tmp[-1] == '(':
            raise SyntaxError('存在多餘的"("!')
        exp.append(tmp.pop())
            
    return exp
                                  

if __name__ == '__main__':
    # postfix = ['3', '5', '-', '6', '17', '4', '*', '+', '*', '3', '/']
    # print(PostfixExpressionCounter(postfix))
    
    # error = ['3', '5', '-', '6', '17', '4', '*', '+', '*', '3', '4']
    # print(PostfixExpressionCounter(error))
    
    infix = ['(', '3', '-', '5', ')', '*', '(', '6', '+', '17', '*','4', ')', 
             '/', '3']
    print(infix_to_postfix(infix))

字尾表示式的計算

計算字尾表示式的主要思想就是利用棧才快取運算物件,在遇到運算子時彈出運算物件進行計算,計算完成以後再次壓入棧。

Python實現:

為了讓函式更加穩健,筆者在這裡增加了結構判斷,如果計算結束後棧內元素個數不為1,那麼一定出現了表示式錯誤。

def PostfixExpressionCounter(s):
    nums = []
    for i in s:
        if i not in '+-*/':
            nums.append(int(i))
        else:
            num2 = nums.pop()
            num1 = nums.pop()
        
            if i == '-':
                nums.append(num1-num2)
            elif i == '+':
                nums.append(num1+num2)
            elif i == '/':
                nums.append(num1/num2)
            else:
                nums.append(num1*num2)
                
    if len(nums) == 1:
        return nums[0]
    raise SyntaxError('表示式結構錯誤!')


if __name__ == '__main__':
    s = ['3', '5', '-', '6', '17', '4', '*', '+', '*', '3', '/']
    print(PostfixExpressionCounter(s))
    
    error = ['3', '5', '-', '6', '17', '4', '*', '+', '*', '3', '4']
    print(PostfixExpressionCounter(error))

括號問題——有效括號判定

問題來源:

力扣:20. 有效的括號

問題簡述:

判斷一個由括號組成的字串,是否為有效的括號組合(每一個左括號都有對應的右括號,且順序正確)。

問題分析:

由於括號存在巢狀,所以不能立即判斷左括號是否有對應的右括號存在,因此需要一個快取空間來儲存左括號。

Python實現:

class Solution:
    def isValid(self, s: str) -> bool:
        if len(s) % 2 != 0: return False

        left, right = {'(': 1, '[': 2, '{':3}, {')': 1, ']': 2, '}':3}
        stack = []
        for i in range(len(s)):
            if not stack:
                stack.append(s[i])
                continue
            if left.get(stack[-1], -1) == right.get(s[i], -2):
                stack.pop()
            else:
                stack.append(s[i])

        return stack == []

參考資料

  1. 裘宗燕.資料結構與演算法——Python語言描述[M].北京:機械工業出版社,2015: 145-150。