基本數據結構——線性結構(棧)

1.什麼是線性結構

線性結構是一種有序數據項的集合,其中每個數據項都有唯一的前驅和後繼(除了第一個沒有前驅,最後一個沒有後繼)。新的數據項加入到數據集中時,只會加入到原有某個數據項之前或之後。具有這種性質的數據集,就稱爲線性結構。
不同線性結構的關鍵區別在於數據項增減的方式。有的結構只允許數據項從一端添加,而有的機構則允許數據項從兩端移除。

2.從4個最簡單但功能強大的結構入手,開始研究數據結構(棧Stack、隊列Queue、雙端隊列DQeque、列表List)

2.1 棧

2.1.1 什麼是棧?

一種有次序的數據項集合。在棧中,數據項的加入和移除僅發生在同一端。這一端叫棧頂(top),另一端叫棧底(base)
距離棧底越近的數據項留在棧中的時間越長,而新加入棧的數據項會被最先移除。這種次序通常稱爲“後進先出”(Last in First out,LIFO)。這是一種基於數據項保存時間的次序,時間越短的離棧頂越近,而時間越長的離棧底越近。
在這裏插入圖片描述

2.1.2 抽象數據類型“棧”定義的操作

在這裏插入圖片描述

操作樣例

在這裏插入圖片描述

2.1.3 用python實現ADT Stack

#定義ADT Stack
class Stack:
    def __init__(self):
        self.items=[]#產生一個空棧
    def isEmpty(self):
        return self.items ==[]#判斷棧是否爲空
    def push(self,item):
        self.items.append(item)#將數據項加入棧頂
    def pop(self):
        return self.items.pop()#將棧頂數據移除
    def peek(self):
        return self.items[len(self.items)-1]#返回棧頂的數據項
    def size(self):
        return len(self.items)#返回棧的大小
    
#stack測試代碼
s=Stack()
print(s.isEmpty())
s.push(4)
s.push('dog')
print(s.peek())
s.push(True)
print(s.size())
print(s.isEmpty())
s.push(8.4)
print(s.pop())
print(s.pop())
print(s.size())

2.1.4 棧的應用:簡單括號匹配

如何構造括號匹配識別算法?

從左到右掃描括號串,最新打開的左括號應該匹配最先遇到的有括號。這樣,第一個左括號(最先打開)就應該匹配最後一個右括號(最後遇到)。這種反轉的識別,正好符合棧的特性。
在這裏插入圖片描述

#定義ADT Stack
class Stack:
    def __init__(self):
        self.items=[]#產生一個空棧
    def isEmpty(self):
        return self.items ==[]#判斷棧是否爲空
    def push(self,item):
        self.items.append(item)#將數據項加入棧頂
    def pop(self):
        return self.items.pop()#將棧頂數據移除
    def peek(self):
        return self.items[len(self.items)-1]#返回棧頂的數據項
    def size(self):
        return len(self.items)#返回棧的大小
#括號匹配問題   
def parChecker(symbolString):
    s=Stack()#創建一個空棧
    balanced=True#爲True表示匹配
    index=0
    while index<len(symbolString) and balanced:
        symbol=symbolString[index]
        if symbol=="(":
            s.push(symbol)#入棧
        else:
            if s.isEmpty():#判斷棧是否爲空
                balanced=False
            else:
                s.pop()
        index=index+1
    if balanced and s.isEmpty():
        return True
    else:
        return False
print(parChecker('((()))'))
print(parChecker('(()'))

通用括號匹配算法

#定義ADT Stack
class Stack:
    def __init__(self):
        self.items=[]#產生一個空棧
    def isEmpty(self):
        return self.items ==[]#判斷棧是否爲空
    def push(self,item):
        self.items.append(item)#將數據項加入棧頂
    def pop(self):
        return self.items.pop()#將棧頂數據移除
    def peek(self):
        return self.items[len(self.items)-1]#返回棧頂的數據項
    def size(self):
        return len(self.items)#返回棧的大小
#通用括號匹配問題   
def parChecker(symbolString):
    s=Stack()#傳建一個空棧
    balanced=True#爲True表示匹配
    index=0
    while index<len(symbolString) and balanced:
        symbol=symbolString[index]
        if symbol in"([{":
            s.push(symbol)
        else:
            if s.isEmpty():
                balanced=False
            else:
                top=s.pop()
                if not matches(top,symbol):
                       balanced=False
        index=index+1
    if balanced and s.isEmpty():
        return True
    else:
        return False
def matches(open,close):
    opens="([{"
    closers=")]}"
    return opens.index(open)==closers.index(close)
print(parChecker('{{([][])}()}'))
print(parChecker('([)'))

2.1.5 棧的應用:十進制轉二進制

class Stack:
    def __init__(self):
        self.items=[]#產生一個空棧
    def isEmpty(self):
        return self.items ==[]
    def push(self,item):
        self.items.append(item)
    def pop(self):
        return self.items.pop()
    def peek(self):
        return self.items[len(self.items)-1]
    def size(self):
        return len(self.items)
#十進制轉二進制
def divideBy2(number):
    remstack=Stack()
    while number>0:
        rem=number%2 #number除以2取餘數
        remstack.push(rem)
        number=number//2#number除以2求商
        #將保存在棧中的數據輸出
    binString=""
    while not remstack.isEmpty():
        binString=binString+str(remstack.pop())
    return binString
        
print(divideBy2(42)) 

十進制轉十六進制以下的任意進制

#定義ADT Stack
class Stack:
    def __init__(self):
        self.items=[]#產生一個空棧
    def isEmpty(self):
        return self.items ==[]
    def push(self,item):
        self.items.append(item)
    def pop(self):
        return self.items.pop()
    def peek(self):
        return self.items[len(self.items)-1]
    def size(self):
        return len(self.items)
#十進制轉爲十六進制以下的任意進制
def baseConverter(number,base):
    digits="0123456789ABCDEF"
    remstack=Stack()
    while number>0:
        rem=number%base #number除以base取餘數
        remstack.push(rem)
        number=number//base#number除以base求商
        #將保存在棧中的數據輸出
    newString=""
    while not remstack.isEmpty():
        newString=newString+digits[remstack.pop()]
    return newString

print(baseConverter(25,2))#十進制轉二進制
print(baseConverter(25,8))#十進制轉八進制
print(baseConverter(25,16))#十進制轉十六進制

2.1.6 棧的應用:通用的中綴轉後綴算法

我們通常看到的表達式像這樣:BC,很容易知道這是B乘以C。這種操作符(operator)介於操作數(operand)中間的表示法稱爲中綴表示法。但是有時候中綴表示法會引起混淆,如"A+BC"表示A+B然後再乘以C還是B*C然後再加A?
例如中綴表達式A+B
將操作符移到前面,變成“+AB”;
或者將操作符一道最後,變爲“AB+”
我們就得到了表達式的另外兩種表示法:“前綴”和“後綴”表示法(以操作符相對於操作數的位置來定義)。
舉例
在這裏插入圖片描述

中綴表達式轉換爲前綴和後綴形式的步驟

(1)將中綴表達式轉換爲全括號形式
(2)將所有的操作符移動到子表達式所在左括號(前綴)或者右括號(後綴)處,替代之,再刪除所有括號。

通用的中綴轉後綴算法

後面的算法描述中,約定中綴表達式是由空格隔開的一系列單詞(token)構成。操作符單詞包括*/±(),而操作數單次則是單字母標識符A、B、C等
(1)創建空棧opstack用於暫存操作符,空表postfixList用於保存後綴表達式。
(2)將中綴表達式轉換爲單詞(token)列表
A+BC=split=>[‘A’,’+’,‘B’,’’,‘C’]
(3)從左到右掃描中綴表達式單詞列表

  • 如果單詞是操作數,則直接添加到後綴表達式列表的末尾
  • 如果單詞是左括號“(”,則壓入opstack棧頂
  • 如果單詞是右括號“)”,則反覆彈出opstack棧頂操作符,加入到輸出列表末尾,直到碰到左括號
  • 如果單詞是操作符“*/±”,則壓入opstack棧頂(但是咋壓入之前,要比較其與棧頂操作符的優先級,如果棧頂的高於或低於它,就要反覆彈出棧頂操作符,加入到輸出列表末尾,直到棧頂的操作符優先級低於它)
    (4)中綴表達式單詞列表掃描結束後,把opstack棧中的所有剩餘操作符依次彈出,添加到輸出列表末尾。
    (5)把輸出列表再用join方法合併成後綴表達式字符串,算法結束。
實例

在這裏插入圖片描述

python代碼實現
#定義ADT Stack
class Stack:
    def __init__(self):
        self.items=[]#產生一個空棧
    def isEmpty(self):
        return self.items ==[]#判斷棧是否爲空
    def push(self,item):
        self.items.append(item)#將數據項加入棧頂
    def pop(self):
        return self.items.pop()#將棧頂數據移除
    def peek(self):
        return self.items[len(self.items)-1]#返回棧頂的數據項
    def size(self):
        return len(self.items)#返回棧的大小
#通用的中綴表達式轉爲後綴表達式
def infixToPostfix(infixexpr):
    prec={}
    #記錄操作符優先級
    prec["*"]=3
    prec["/"]=3
    prec["+"]=2
    prec["-"]=2
    prec["("]=1
    opStack=Stack()
    postfixList=[]
    tokenList=infixexpr.split()#解析表達式到單詞列表
    for token in tokenList:
        #操作數
        if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":#如果是操作數直接添加到後綴表達式列表的末尾
            postfixList.append(token)
        elif token=='(':# 如果單詞是左括號“(”,則壓入opstack棧頂
            opStack.push(token)
        elif token==')':# 如果單詞是右括號“)”,則反覆彈出opstack棧頂操作符,加入到輸出列表末尾,直到碰到左括號
            topToken=opStack.pop()
            while topToken !='(':
                postfixList.append(topToken)
                topToken=opStack.pop()
        else:#操作符
            while (not opStack.isEmpty()) and \
                (prec[opStack.peek()]>=prec[token]):
                postfixList.append(opStack.pop())
            opStack.push(token)
    while not opStack.isEmpty():#把opstack棧中的所有剩餘操作符依次彈出,添加到輸出列表末尾。
        postfixList.append(opStack.pop())
    return " ".join(postfixList)#合成後綴表達式字符串
    
print(infixToPostfix('A * B + C * D'))
2.1.7 後綴表達式求值問題

在對後綴表達式從左到右掃描的過程中,由於操作符在操作數後面,所以要暫存操作數,在碰到操作符的時候再將暫存的兩個操作數進行實際的計算。

實例

在這裏插入圖片描述
在這裏插入圖片描述

後綴表達式求值算法流程

(1)創建空棧operandStack用於暫存操作數
(2)將後綴表達式用split方法解析爲單詞(token)的列表
(3)從左到右掃描單詞列表

  • 如果但此時一個操作數,將單詞轉換爲整數int,壓入operandStack棧頂
  • 如果單詞是一個操作符(*/±),就開始求值,從棧頂彈出2個操作數,先彈出的是右操作數,後彈出的是左操作數,計算後將值重新呀入棧頂。
    (4)單詞列表掃描結束後,表達式的值就在棧頂
    (5)彈出棧頂的值,返回
python代碼實現
#定義ADT Stack
class Stack:
    def __init__(self):
        self.items=[]#產生一個空棧
    def isEmpty(self):
        return self.items ==[]#判斷棧是否爲空
    def push(self,item):
        self.items.append(item)#將數據項加入棧頂
    def pop(self):
        return self.items.pop()#將棧頂數據移除
    def peek(self):
        return self.items[len(self.items)-1]#返回棧頂的數據項
    def size(self):
        return len(self.items)#返回棧的大小
    
#後綴表達式求值
def postfixEval(postfixExpr):
    operandStack=Stack()
    tokenList=postfixExpr.split()
    for token in tokenList:
        if token in "0123456789":#如果是操作數,將單詞轉爲整數int,壓入operandStack棧頂
            operandStack.push(int(token))
        else:
            operand2=operandStack.pop()#操作符右邊的操作數
            operand1=operandStack.pop()#操作符左邊的操作數
            result=doMath(token,operand1,operand2)
            operandStack.push(result)
    return operandStack.pop()

def doMath(op,op1,op2):
    if op=="*":
        return op1*op2
    if op=="/":
        return op1/op2
    if op=="+":
        return op1+op2
    else:
        return op1-op2
    
print(postfixEval("4 5 6 * +"))       
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章