想象一下,你每天早上用的盤子,剛洗完的盤子會疊放在一起。最上面的盤子是你剛放上去的,要拿盤子的時候,你必須先把最上面的那個拿走,對吧?這個“先放的在下面,後放的在上面,拿的時候先拿上面的”的過程,其實就是我們今天要講的數據結構——棧的核心特性。
一、棧是什麼?¶
棧(Stack)是一種特殊的線性表,它的特殊之處在於:只能從一端進行插入和刪除操作。我們把這個操作的一端叫做“棧頂”,另一端叫做“棧底”。就像疊盤子,棧底是最下面的盤子,棧頂是最上面的盤子;只能在棧頂放盤子(入棧)或拿盤子(出棧),不能從棧底操作。
二、核心特性:後進先出(LIFO)¶
棧最關鍵的特性是“後進先出”(Last In First Out,簡稱LIFO)。意思是:最後放入棧的元素,會最先被取出。
舉個生活例子:
- 你把彈夾裏的子彈一顆顆壓進去,最後壓進去的子彈在最上面。開槍時,你會先射出最上面的那顆子彈(最後壓進去的),這就是“後進先出”。
- 疊被子:週一疊的被子在最下面,週二疊的在上面,你整理時會先疊週二的被子(後疊的),這也是LIFO。
對比一下隊列(先進先出,FIFO):排隊買票時,先到的人先買到票(先入先出);而棧就像“插隊”,最後插隊的人反而先被處理。
三、棧的基本操作¶
棧只有兩個核心操作:入棧(push) 和 出棧(pop),還可以查看棧頂元素(top)或判斷棧是否爲空(empty)。
- 入棧(push):往棧頂添加一個元素。
比如:你往盤子堆裏放一個新盤子,這個盤子會成爲新的棧頂。 - 出棧(pop):從棧頂移除一個元素,並返回該元素。
比如:你從盤子堆裏拿走最上面的盤子,這個盤子就是棧頂元素,拿走後棧頂變成下一個盤子。 - 查看棧頂(top):不刪除元素,只看棧頂的元素(比如你想看看最上面的盤子是什麼樣的)。
- 判空(empty):檢查棧裏是否還有元素(比如你想知道盤子堆是否空了)。
四、棧的原理圖解(用例子理解)¶
假設我們按順序執行以下操作,用數字標記入棧順序,用箭頭表示出棧順序:
-
初始狀態:棧爲空(用
[]表示,[]中左邊是棧底,右邊是棧頂)。
棧:[] -
入棧1(push 1):往棧頂加1,此時棧頂是1。
棧:[1] -
入棧2(push 2):往棧頂加2,此時棧頂是2(1在棧底,2在棧頂)。
棧:[1, 2] -
入棧3(push 3):往棧頂加3,此時棧頂是3(順序:1→2→3,棧底到棧頂)。
棧:[1, 2, 3] -
出棧(pop):從棧頂拿走3,此時棧頂變成2。
棧:[1, 2]
(彈出元素:3) -
出棧(pop):從棧頂拿走2,此時棧頂變成1。
棧:[1]
(彈出元素:2) -
出棧(pop):從棧頂拿走1,此時棧爲空。
棧:[]
(彈出元素:1)
五、棧的實際應用場景¶
棧在編程和生活中無處不在,以下是幾個經典例子:
- 括號匹配:比如判斷表達式
(a + b) * (c - d)是否合法。用棧記錄左括號,遇到右括號就彈出棧頂的左括號,最後棧空則合法(否則括號不匹配)。 - 函數調用棧:當你寫一個程序時,調用
main函數,main裏調用func1,func1裏調用func2,func2執行完後會先返回func1,func1執行完返回main。這就是棧的“後進先出”特性(後調用的函數先返回)。 - 瀏覽器後退功能:你打開網頁1→網頁2→網頁3,後退時會依次回到網頁2→網頁1,相當於彈出棧頂元素(網頁3→網頁2→網頁1)。
- 表達式求值:比如計算
3 + 4 * 2 / (1 - 5),需要用棧處理運算符優先級,先算括號、再乘除、最後加減。
六、總結¶
棧是一種“後進先出”的線性結構,只能從棧頂操作元素。它的核心是“LIFO”,在括號匹配、函數調用、瀏覽器後退等場景中發揮關鍵作用。記住:棧就像疊盤子,後放的先拿,先放的後拿。
理解棧的基本操作和特性後,你會發現它是解決很多問題的“利器”,比如後續學習遞歸、動態規劃時,棧也會經常被用到。
(PS:如果覺得抽象,可以拿個筆筒模擬:把筆從下往上依次插入,每次拿最上面的筆,就是棧的“後進先出”啦!)