1. 程式人生 > >資料結構與算法系列6--棧

資料結構與算法系列6--棧

什麼是棧?

1.後進者先出,先進者後出,這就是典型的“棧”結構。
2.從棧的操作特性來看,是一種“操作受限”的線性表,只允許在端插入和刪除資料。

什麼時候使用?

當某個資料集合只涉及在某端插入和刪除資料,且滿足後進者先出,先進者後出的操作特性時,我們應該首選棧這種資料結構。

棧的陣列實現(自動擴容)

時間複雜度分析:根據均攤複雜度的定義,可以得陣列實現(自動擴容)符合大多數情況是O(1)級別複雜度,個別情況是O(n)級別複雜度,比如自動擴容時,會進行完整資料的拷貝。
空間複雜度分析:在入棧和出棧的過程中,只需要一兩個臨時變數儲存空間,所以O(1)級別。
注意: 我們說空間複雜度的時候,是指除了原本的資料儲存空間外,演算法執行還需要額外的儲存空間。
程式碼實現:

棧的連結串列實現

時間複雜度分析:壓棧和彈棧的時間複雜度均為O(1)級別,因為只需更改單個節點的索引即可。
空間複雜度分析:在入棧和出棧的過程中,只需要一兩個臨時變數儲存空間,所以O(1)級別。我們說空間複雜度的時候,是指除了原本的資料儲存空間外,演算法執行還需要額外的儲存空間。
程式碼實現:

棧的應用

1.棧在函式呼叫中的應用
作業系統給每個執行緒分配了一塊獨立的記憶體空間,這塊記憶體被組織成“棧”這種結構,用來儲存函式呼叫時的臨時變數。每進入一個函式,就會將其中的臨時變數作為棧幀入棧,當被呼叫函式執行完成,返回之後,將這個函式對應的棧幀出棧。
2.棧在表示式求值中的應用(比如:34+13*9+44-12/3)


利用兩個棧,其中一個用來儲存運算元,另一個用來儲存運算子。我們從左向右遍歷表示式,當遇到數字,我們就直接壓入運算元棧;當遇到運算子,就與運算子棧的棧頂元素進行比較,若比運算子棧頂元素優先順序高,就將當前運算子壓入棧,若比運算子棧頂元素的優先順序低或者相同,從運算子棧中取出棧頂運算子,從運算元棧頂取出2個運算元,然後進行計算,把計算完的結果壓入運算元棧,繼續比較。
3.棧在括號匹配中的應用(比如:{}{()})
用棧儲存為匹配的左括號,從左到右一次掃描字串,當掃描到左括號時,則將其壓入棧中;當掃描到右括號時,從棧頂取出一個左括號,如果能匹配上,則繼續掃描剩下的字串。如果掃描過程中,遇到不能配對的右括號,或者棧中沒有資料,則說明為非法格式。
當所有的括號都掃描完成之後,如果棧為空,則說明字串為合法格式;否則,說明未匹配的左括號為非法格式。
4.如何實現瀏覽器的前進後退功能?

我們使用兩個棧X和Y,我們把首次瀏覽的頁面依次壓如棧X,當點選後退按鈕時,再依次從棧X中出棧,並將出棧的資料一次放入Y棧。當點選前進按鈕時,我們依次從棧Y中取出資料,放入棧X中。當棧X中沒有資料時,說明沒有頁面可以繼續後退瀏覽了。當Y棧沒有資料,那就說明沒有頁面可以點選前進瀏覽了。

思考

我們在講棧的應用時,講到用函式呼叫棧來儲存臨時變數,為什麼函式呼叫要用“棧”來儲存臨時變數呢?用其他資料結構不行嗎?
因為函式呼叫的執行順序符合後進者先出,先進者後出的特點。比如函式中的區域性變數的生命週期的長短是先定義的生命週期長,後定義的生命週期短;還有函式中呼叫函式也是這樣,先開始執行的函式只有等到內部呼叫的其他函式執行完畢,該函式才能執行結束。
正是由於函式呼叫的這些特點,根據資料結構是特定應用場景的抽象的原則,我們優先考慮棧結構。

基於連結串列實現的棧

'''基於連結串列實現的棧'''
class Node():
    def __init__(self,data,next=None):
        self.data=data
        self.next=next

class LinkedStack():
    def __init__(self):
        #定義棧頂指標
        self.top=None

    def push(self,data):
        node=Node(data)
        if self.top:
            node.next=self.top
            self.top=node
        else:
            self.top=node

    def pop(self):
        if self.top:
            data=self.top.data
            self.top=self.top.next
            return data
        else:
            return False

    def __repr__(self):
        data=[]
        current=self.top
        while current:
            data.append(current.data)
            current=current.next

        return "->".join(value for value in data)



if __name__=="__main__":
    stack=LinkedStack()
    for i in range(6):
        stack.push(str(i))

    print(stack)
    for i in range(7):
        print("彈出"+str(stack.pop()))
        print(stack)