利用棧實現四則運算表達式求值----先將中綴表達式轉換成后綴表達式,然后再求后綴表達式的值


利用棧實現四則運算表達式求值,附Python代碼

中綴表達式和后綴表達式

平時用到的標准的四則運算表達式就叫做中綴表達式,例如“9 +(3 - 1) * 3 + 10 / 2)”,特點是運算符在數字中間;

后綴表達式就是一種把運算符放在數字后面的形式,“9 3 1 - 3 * + 10 2 / +”即為上例中綴表達式對應的后綴表達式形式,后綴表達式還有一個特點就是消除了所有的括號;

中綴表達式能夠非常直觀地展示出運算關系,很方便手動計算,但是如果要設計一個計算機程序來計算這個表達式卻變得非常繁瑣,不僅要考慮四則運算的優先級,還要考慮括號的影響,而后綴表達式雖然在表現形式上不直觀卻非常便於計算機進行計算。

后綴表達式計算結果

后綴表達式的計算要借助棧來實現。

規則:從左到右遍歷表達式的每個數字和符號,遇到的是數字就進棧,遇到的時符號就將棧頂的兩個數字出棧進行計算,然后將計算結果入棧,最終棧里的值即為計算的結果。

以中綴表達式“9 +(3 - 1) * 3 + 10 / 2)”的后綴表達式“9 3 1 - 3 * + 10 2 / +”為例,計算過程如下:

1、初始化一個空棧

2、后綴表達式前三個都是數字,所以9、3、1依次入棧,此時棧內元素為(最左邊為棧底元素):9 3 1

3、接下來為減號“-”,此時1出棧作為第二個數,3出棧作為第一個數(因為棧內元素是先進后出,所以最先出棧的為運算中的第二個數,接着出棧的才是運算中的第一個數),進行進行減法運算3 - 1 = 2然后入棧,此時棧內元素為:9 2

4、接下來為數字3,進棧,棧內元素為:9 2 3

5、接下來為乘號“*”,此時3、2出棧,進行乘法運算2 * 3 = 6,結果入棧,此時棧內元素為:9 6

6、接下來是加號“+”,9、6出棧進行加法運算,結果15入棧,此時棧內元素為:15

7、接下來是數字10、2入棧,此時棧內元素為:15 10 2

8、接下來是除號“/”,10、2出棧進行除法運算,結果5入棧,此時棧內元素為:15 5

9、最后一個是加號“+”,15、5出棧進行加法運算,結果20即為整個表達式的運算結果,與中綴表達式計算結果一致

只需要按順序遍歷就能夠計算出結果,不用考慮對四則運算法則做復雜的邏輯處理,對計算機處理來說是非常方便的,這樣一來,最重要的問題就是怎樣將中綴表達式轉化為后綴表達式。

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

中綴表達式轉化為后綴表達式同樣要借助棧來實現,不同於中綴表達式的計算,這里的棧用於存貯運算符號而不是數值。

規則:從左到右遍歷中綴表達式中的每個數字和符號,若是數字就輸出,即成為后綴表達式的一部分;若是符號則要分為兩種情況:

1)是括號時,如果是左括號,直接將左括號入棧,如果是右括號則棧頂元素依次出棧並輸出,直到有一個左括號出棧(出棧的左括號不輸出到后綴表達式)。

2)是運算符號時,如果棧頂符號為左括號,則直接將這個運算符號入棧。棧頂符號不為左括號時,如果該運算符號優先級比棧頂運算符號高則入棧,比棧頂符號低或者相等時則棧頂元素依次出棧並輸出直到棧為空或者棧頂為左括號為止,然后將這個符號入棧

最后將棧頂符號依次出棧並輸出,得到的結果即為最終的后綴表達式。

同樣以中綴表達式“9 +(3 - 1) * 3 + 10 / 2)”為例推導其后綴表達式的步驟如下:

1、初始化一個空棧

2、第一個數字9直接輸出到后綴表達式,第二個符號“+”入棧,此時后綴表達式:9    棧內符號:+

3、接下里左括號入棧,數字3輸出,此時后綴表達式為: 9 3    棧內元素為:+ (

4、接下來是減號“-”,由於棧頂為左括號,直接入棧,數字1輸出,此時后綴表達式:9 3 1    棧內符號:+ ( -

5、接下來是右括號,棧頂符號“-”出棧並輸出,接着棧頂左括號出棧(括號匹配的處理),此時后綴表達式:9 3 1 -   棧內符號:+

6、接下來是乘號“*”,比棧頂符號加號“+”優先級高(在出stack的過程中去處理優先級,優先級高的后綴表達式靠前),入棧,數字3輸出,此時后綴表達式:9 3 1 - 3    棧內符號:+ *

7、接下來是加號“+”,比棧頂“*”優先級低,棧頂“*”出棧並輸出,接着與新的棧頂加號“+”比較優先級相同(貪心思路求最高優先級的),棧頂“+”出棧並輸出,棧為空,到此為止,然后將加號“+”入棧。此時后綴表達式為:9 3 1 - 3 * +    棧內符號:+

8、接下來數字10輸出,除號“/”優先級比棧頂加號“+”優先級高直接入棧,此時后綴表達式:9 3 1 - 3 * + 10   棧內為:+ /

9、最后數字2輸出,遍歷結束后,棧頂符號依次出棧並輸出,得到最終的后綴表達式:9 3 1 - 3 * + 10 2 / +

優化與整合

通過前面的介紹,只需要經過將中綴表達式轉化為后綴表達式,再計算后綴表達式這兩步就能得到一個四則運算表達式的值。這兩步中各用到了一個棧,推導后綴表達式時用到的棧存儲的是運算符號以及括號(只有左括號,沒有右括號),計算后綴表達式時用到的棧存儲的是數字,兩個步驟分開先后執行要遍歷一次中綴表達式載遍歷一遍后綴表達式。同時在中綴表達式推導后綴表達式以及計算后綴表達式的過程中,都是要對字符串進行操作,如果將兩步合並同時執行的話,不僅能夠簡化代碼,更能提高運算效率,因為這樣全程只需要遍歷一次中綴表達式就可以完成計算。

思路:用一個棧data保存運算數字,一個棧opt保存運算符號。從左到右遍歷中綴表達式,如果是數字就入棧data,如果是符號,以下四種情況直接將符號入棧opt:1)棧為空;2)棧頂為左括號;3)該符號為左括號;4)該運算符號優先級比棧頂符號高。如果是右括號,則執行一次計算步驟:從opt出棧一個運算符號,從data出棧兩個數字進行一次運算並將結果入棧data。重復執行該計算步驟,直到opt棧頂為左括號,然后將該左括號出棧;如果該符號優先級低於opt棧頂符號或者與棧頂符號優先級相同時,重復執行與之前相同的計算步驟,直到opt棧為空,若中途opt棧頂符號為左括號則停止執行計算步驟。中綴表達式遍歷完成后,繼續執行之前的計算步驟直到opt棧為空。

因為Python處理字符串比較簡潔方便,代碼使用Python3編寫,為輸入方便起見,其中輸入的表達式字符串中不包含空格,代碼中忽略了對輸入表達式不合法的處理,假設輸入表達式是可以正確計算的:

def compare(op1, op2):
    """
    比較兩個運算符的優先級,乘除運算優先級比加減高
    op1優先級比op2高返回True,否則返回False
    """
    return op1 in ["*", "/"] and op2 in ["+", "-"]
 
 
def getvalue(num1, num2, operator):
    """
    根據運算符號operator計算結果並返回
    """
    if operator == "+":
        return num1 + num2
    elif operator == "-":
        return num1 - num2
    elif operator == "*":
        return num1 * num2
    else:  # /
        return num1 / num2
 
 
def process(data, opt):
    """
    opt出棧一個運算符,data出棧兩個數值,進行一次計算,並將結果入棧data
    """
    operator = opt.pop()
    num2 = data.pop()
    num1 = data.pop()
    data.append(getvalue(num1, num2, operator))
 
 
def calculate(s):
    """
    計算字符串表達式的值,字符串中不包含空格
    """
    data = []  # 數據棧
    opt = []  # 操作符棧
    i = 0  # 表達式遍歷索引
    while i < len(s):
        if s[i].isdigit():  # 數字,入棧data
            start = i  # 數字字符開始位置
            while i + 1 < len(s) and s[i + 1].isdigit():
                i += 1
            data.append(int(s[start: i + 1]))  # i為最后一個數字字符的位置
        elif s[i] == ")":  # 右括號,opt出棧同時data出棧並計算,計算結果入棧data,直到opt出棧一個左括號
            while opt[-1] != "(":
                process(data, opt)
            opt.pop()  # 出棧"("
        elif not opt or opt[-1] == "(":  # 操作符棧為空,或者操作符棧頂為左括號,操作符直接入棧opt
            opt.append(s[i])
        elif s[i] == "(" or compare(s[i], opt[-1]):  # 當前操作符為左括號或者比棧頂操作符優先級高,操作符直接入棧opt
            opt.append(s[i])
        else:  # 優先級不比棧頂操作符高時,opt出棧同時data出棧並計算,計算結果如棧data
            while opt and not compare(s[i], opt[-1]):
                if opt[-1] == "(":  # 若遇到左括號,停止計算
                    break
                process(data, opt)
            opt.append(s[i])
        i += 1  # 遍歷索引后移
    while opt:
        process(data, opt)
    print(data.pop())
 
 
if __name__ == '__main__':
    exp = "(9+((3-1)*3+10/2))*2"
    calculate(exp)
 

  




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM