一、什么是棧?
1.后進者先出,先進者后出,這就是典型的“棧”結(jié)構(gòu)。
2.從棧的操作特性來看,是一種“操作受限”的線性表,只允許在端插入和刪除數(shù)據(jù)。
二、為什么需要棧?
1.棧是一種操作受限的數(shù)據(jù)結(jié)構(gòu),其操作特性用數(shù)組和鏈表均可實現(xiàn)。
2.但,任何數(shù)據(jù)結(jié)構(gòu)都是對特定應用場景的抽象,數(shù)組和鏈表雖然使用起來更加靈活,但卻暴露了幾乎所有的操作,難免會引發(fā)錯誤操作的風險。
3.所以,當某個數(shù)據(jù)集合只涉及在某端插入和刪除數(shù)據(jù),且滿足后進者先出,先進者后出的操作特性時,我們應該首選棧這種數(shù)據(jù)結(jié)構(gòu)。
三、如何實現(xiàn)棧?
"""
Stack based upon linked list
基于鏈表實現(xiàn)的棧

Author: Wenru

"""

 from typing import Optional
 class Node:
    
    def __init__(self, data: int, next=None):
        self._data = data
        self._next = next
    
 class LinkedStack:
    """A stack based upon singly-linked list.
    """
    def __init__(self):
        self._top: Node = None
    
    def push(self, value: int):
        new_top = Node(value)
        new_top._next = self._top
        self._top = new_top
    
    def pop(self) -> Optional[int]:
        if self._top:
            value = self._top._data
            self._top = self._top._next
            return value
    
    def __repr__(self) -> str:
        current = self._top
        nums = []
        while current:
            nums.append(current._data)
            current = current._next
        return " ".join(f"{num}]" for num in nums)
 if __name__ == "__main__":
    stack = LinkedStack()
    for i in range(9):
        stack.push(i)
    print(stack)
    for _ in range(3):
        stack.pop()
    print(stack) 

四、棧的應用
1.棧在函數(shù)調(diào)用中的應用
操作系統(tǒng)給每個線程分配了一塊獨立的內(nèi)存空間,這塊內(nèi)存被組織成“?!边@種結(jié)構(gòu),用來存儲函數(shù)調(diào)用時的臨時變量。每進入一個函數(shù),就會將其中的臨時變量作為棧幀入棧,當被調(diào)用函數(shù)執(zhí)行完成,返回之后,將這個函數(shù)對應的棧幀出棧。
2.棧在表達式求值中的應用(比如:34+13*9+44-12/3)
利用兩個棧,其中一個用來保存操作數(shù),另一個用來保存運算符。我們從左向右遍歷表達式,當遇到數(shù)字,我們就直接壓入操作數(shù)棧;當遇到運算符,就與運算符棧的棧頂元素進行比較,若比運算符棧頂元素優(yōu)先級高,就將當前運算符壓入棧,若比運算符棧頂元素的優(yōu)先級低或者相同,從運算符棧中取出棧頂運算符,從操作數(shù)棧頂取出2個操作數(shù),然后進行計算,把計算完的結(jié)果壓入操作數(shù)棧,繼續(xù)比較。
3.棧在括號匹配中的應用(比如:{}{()})
用棧保存為匹配的左括號,從左到右一次掃描字符串,當掃描到左括號時,則將其壓入棧中;當掃描到右括號時,從棧頂取出一個左括號,如果能匹配上,則繼續(xù)掃描剩下的字符串。如果掃描過程中,遇到不能配對的右括號,或者棧中沒有數(shù)據(jù),則說明為非法格式。
當所有的括號都掃描完成之后,如果棧為空,則說明字符串為合法格式;否則,說明未匹配的左括號為非法格式。
4.如何實現(xiàn)瀏覽器的前進后退功能?
我們使用兩個棧X和Y,我們把首次瀏覽的頁面依次壓如棧X,當點擊后退按鈕時,再依次從棧X中出棧,并將出棧的數(shù)據(jù)一次放入Y棧。當點擊前進按鈕時,我們依次從棧Y中取出數(shù)據(jù),放入棧X中。當棧X中沒有數(shù)據(jù)時,說明沒有頁面可以繼續(xù)后退瀏覽了。當Y棧沒有數(shù)據(jù),那就說明沒有頁面可以點擊前進瀏覽了。
五、思考

  1. 我們在講棧的應用時,講到用函數(shù)調(diào)用棧來保存臨時變量,為什么函數(shù)調(diào)用要用“?!眮肀4媾R時變量呢?用其他數(shù)據(jù)結(jié)構(gòu)不行嗎?
    答:因為函數(shù)調(diào)用的執(zhí)行順序符合后進者先出,先進者后出的特點。比如函數(shù)中的局部變量的生命周期的長短是先定義的生命周期長,后定義的生命周期短;還有函數(shù)中調(diào)用函數(shù)也是這樣,先開始執(zhí)行的函數(shù)只有等到內(nèi)部調(diào)用的其他函數(shù)執(zhí)行完畢,該函數(shù)才能執(zhí)行結(jié)束。
    正是由于函數(shù)調(diào)用的這些特點,根據(jù)數(shù)據(jù)結(jié)構(gòu)是特定應用場景的抽象的原則,我們優(yōu)先考慮棧結(jié)構(gòu)。
    2.我們都知道,JVM 內(nèi)存管理中有個“堆?!钡母拍?。棧內(nèi)存用來存儲局部變量和方法調(diào)用,堆內(nèi)存用來存儲 Java 中的對象。那 JVM 里面的“?!备覀冞@里說的“?!笔遣皇且换厥履??如果不是,那它為什么又叫作“?!蹦??
    答:JVM里面的棧和我們這里說的是一回事,被稱為方法棧。和前面函數(shù)調(diào)用的作用是一致的,用來存儲方法中的局部變量。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容