六十3、棧在括號匹配和表達式求值中的應用

圖片


「@Author:Runsen」python

編程的本質來源於算法,而算法的本質來源於數學,編程只不過將數學題進行代碼化。「---- Runsen」git

算法,一門既不容易入門,也不容易精通的學問。github

括號匹配

這是Leetcode第20題,也是一道單調棧的簡單題。面試

給定一個只包括'(',')','{','}','[',']'的字符串,判斷字符串是否有效。正則表達式

有效字符串需知足:算法

  • 左括號必須用相同類型的右括號閉合。
  • 左括號必須以正確的順序閉合。
  • 注意空字符串可被認爲是有效字符串。

輸入: "{[]}"輸出: trueexpress

單調棧關鍵在於如何入棧和出棧。編程

用棧保存爲匹配的左括號,從左到右一次掃描字符串,當掃描到左括號時,則將其壓入棧中;當掃描到右括號時,從棧頂取出一個左括號,若是能匹配上,則繼續掃描剩下的字符串。若是掃描過程當中,遇到不能配對的右括號,或者棧中沒有數據,則說明爲非法格式。app

當全部的括號都掃描完成以後,若是棧爲空,則說明字符串爲合法格式;不然,說明未匹配的左括號爲非法格式。ide

def isValid(s):
    """
    :type s: str
    :rtype: bool
    """

    stack = []
    paren_map ={')':'(',']':'[','}':'{'}
    for c in s:
        if c not in paren_map:
            stack.append(c)
        elif not stack or paren_map[c] !=stack.pop():
            return False
    return not stack
s = input('輸入括號字符:')
print(isValid(s))

在此題中,也能夠利用python種的replace函數將成對的可匹配括號用空字符代替 ,以後依次進行 ,如果有效的括號 ,必然通過有限次循環後 ,字符串爲空 ,則最後判斷字符串是否爲空便可。思路簡單,實現也很容易:

def isValid(s):
    """
    :type s: str
    :rtype: bool
    """

    n = len(s)
    if n == 0return True
    while '()' in s or '{}' in s or '[]' in s:
        s = s.replace('{}','').replace('[]','').replace('()Val','')
    return s == ''

數學表達式

首先,咱們來看一下數學表達式的三種形式:前綴表達式,中綴表達式,後綴表達式。

中綴表達式(Infix Expression)就是咱們平時經常使用的書寫方式,帶有括號。

前綴表達式(Prefix Expression)要求運算符出如今運算數字的前面。

後綴表達式(Postfix Expression)要求運算符出如今運算數字的後面,通常這兩種表達式不出現括號。後綴表達式,又稱逆波蘭式。

數學表達式的三種形式示例以下:

中綴表達式 前綴表達式 後綴表達式
A + B * C + D + + A * B C D A B C * + D +
(A + B) * (C + D) * + A B + C D A B + C D + *
A * B + C * D + * A B * C D A B * C D * +
A + B + C + D + + + A B C D A B + C + D +

中綴表達式操做符是以中綴形式處於操做數的中間(例:3 + 4),中綴表達式是人們經常使用的算術表示方法。與前綴表達式(例:+ 1 2)或後綴表達式(例:1 2 +)相比,中綴表達式不容易被計算機解析,但仍被許多程序語言使用,由於它符合人們的廣泛用法。

下面問題轉爲爲:如何利用棧實現中綴表達式求值,好比:34+13*9+44-12/3=191

思路:利用兩個棧,其中一個用來保存操做數,另外一個用來保存運算符。

咱們從左向右遍歷表達式,當遇到數字,咱們就直接壓入操做數棧;當遇到運算符,就與運算符棧的棧頂元素進行比較。

若比運算符棧頂元素優先級高,就將當前運算符壓入棧,若比運算符棧頂元素的優先級低或者相同,從運算符棧中取出棧頂運算符,從操做數棧頂取出2個操做數,而後進行計算,把計算完的結果壓入操做數棧,繼續比較。

def infix_evaluator(infix_expression : str) -> int :
    '''這是中綴表達式求值的函數
    :參數 infix_expression:中綴表達式 須要用空格進行隔開
    '''

    token_list = infix_expression.split()
    print(token_list)
    # 運算符優先級字典
    pre_dict = {'*':3,'/':3,'+':2,'-':2'(':1}
    # 運算符棧
    operator_stack = []
    # 操做數棧
    operand_stack = []
    for token in token_list:
        # 數字進操做數棧
        print(token)
        # 10或者-10的狀況
        if token.isdecimal() or token[1:].isdecimal(): 
            operand_stack.append(int(token))
        # 左括號進運算符棧
        elif token == '(':
            operator_stack.append(token)
        # 碰到右括號,就要把棧頂的左括號上面的運算符都彈出求值
        elif token == ')':
            top = operator_stack.pop()
            while top != '(':
                # 每彈出一個運算符,就要彈出兩個操做數來求值
                # 注意彈出操做數的順序是反着的,先彈出的數是op2
                op2 = operand_stack.pop()
                op1 = operand_stack.pop()
                # 求出的值要壓回操做數棧
                # 這裏用到的函數get_value在下面有定義
                operand_stack.append(get_value(top,op1,op2))
                # 彈出下一個棧頂運算符
                top = operator_stack.pop()
        # 碰到運算符,就要把棧頂優先級不低於它的都彈出求值
        elif token in '+-*/':
            while operator_stack and pre_dict[operator_stack[-1]] >= pre_dict[token]:
                top = operator_stack.pop()
                op2 = operand_stack.pop()
                op1 = operand_stack.pop()
                operand_stack.append(get_value(top,op1,op2))
            # 別忘了最後讓當前運算符進棧
            operator_stack.append(token)
    # 表達式遍歷完成後,棧裏剩下的操做符也都要求值   
    while operator_stack:
        top = operator_stack.pop()
        op2 = operand_stack.pop()
        op1 = operand_stack.pop()
        operand_stack.append(get_value(top,op1,op2))
    # 最後棧裏只剩下一個數字,這個數字就是整個表達式最終的結果
    print(operand_stack)
    print(operator_stack)
    return operand_stack[0]
 
def get_value(operator : str, op1 : int, op2 : int):
    '''這是四則運算函數
    :參數 operator:運算符
    :參數 op1:左邊的操做數
    :參數 op2:右邊的操做數
    '''

    if operator == '+':
        return op1 + op2
    elif operator == '-':
        return op1 - op2
    elif operator == '*':
        return op1 * op2
    elif operator == '/':
        return op1 / op2
 
# 用一個例子試試,得出告終果  17.0
print(infix_evaluator('9 + ( 3 - 1 * 2 ) * 3 + 10 / 2'))

17.0

上述程序只是使用四則運算表達式進行學習計算,可是輸入須要加空格進行分隔,好比9 + ( 3 - 1 * 2 ) * 3 + 10 / 2分隔爲['9', '+', '(', '3', '-', '1', '*', '2', ')', '*', '3', '+', '10', '/', '2']

後來嘗將9+(3-1*2)*3+10/2分隔爲['9', '+', '(', '3', '-', '1', '*', '2', ')', '*', '3', '+', '10', '/', '2']

後來想到了正則表達式1-9]\d*|[\+\-\*\/\(\)]

import re
s = '9+(3-1*2)*3+10/2'
print(re.findall('[1-9]\d*|[\+\-\*\/\(\)]',s))

['9''+''(''3''-''1''*''2'')''*''3''+''10''/''2']

所以利用棧實現中綴表達式求值中等偏難算法題基本完成。

本文已收錄 GitHub,傳送門~[1] ,裏面更有大廠面試完整考點,歡迎 Star。



Reference

[1]

傳送門~:https://github.com/MaoliRUNsen/runsenlearnpy100





- END -

圖片

相關文章
相關標籤/搜索