定義
棧(stack):是限定僅在表尾進行插入和刪除操作的線性表。
我們允許插入和刪除的一端稱為棧頂(top),另一端稱為棧底(bottom),不含任何資料元素的棧稱為空棧。
棧又稱作後進先出(Last In First Out)的線性表。**這裡應該注意,棧是一種特殊的線性表。**棧的特殊之處在於限制了線性表的插入和刪除的位置。
棧是線性表,那麼就具有線性關係,即前驅後繼關係。
棧底是固定的,最先進棧的只能在棧底。
棧的插入操縱,叫做進棧,也叫壓棧、入棧。棧的刪除操作,也叫出棧,有的也叫彈棧。如圖:
特殊注意,並不是最先進棧的就一定是最後出棧,如1進、1出、2進、2出、3進、3出,1先進,1也先出。
線性表的順序儲存和鏈式儲存,對於棧來說同樣適用。
棧的順序儲存結構
棧是通過陣列來實現的。如圖:
top表示棧頂元素的位置,top為-1表示為空棧,top=StackSize-1表示棧滿。
進棧、出棧
由圖可知,兩者都沒有涉及到任何迴圈操作,因此時間複雜度都是O(1) 。
兩棧共享控制元件
棧有一個很大的缺陷,就是必須事先確定陣列儲存空間的大小,萬一不夠用了,就需要用程式設計手段進行擴充套件陣列容量。
對於兩個相同型別的棧,我們可以盡最大限度地利用事先開闢好的儲存空間來進行操作。
方法就是:兩個棧有兩個棧底,讓一個棧的棧底為陣列的始端,即下標為0處,另一個棧底為陣列的末端,即下標為陣列長度n-1處。這樣,如果兩個棧增加元素,就是兩個端點向中間延伸。
top1和top2是棧1和棧2的兩個棧頂指標,只要它們不見面,兩個棧就可以一直用。
一般是當兩個棧的空間需求有相反關係時,也就是一個棧增長,另一個棧縮短的情況。
棧的鏈式儲存結構
棧的鏈式儲存結構,簡稱鏈棧。
由於單連結串列有頭指標,而棧頂指標也是必須的,所以最好的辦法就是把棧頂放在單連結串列的頭部。如圖:
對於鏈棧來說,基本不存在滿棧的情況,除非記憶體已經沒有可以使用的空間。
鏈棧的進棧和出棧
直接看圖:
進棧
新結點S指標域指向原棧頂,將S賦值給棧頂指標。
出棧
棧頂結點指標指向下一位,釋放結點P。
綜上,如果棧的使用過程中,元素變化不可預料,有時很小,有時非常大,那麼最好用鏈棧,反之,如果它的變化在可控範圍內,建議使用順序棧更好。
棧的應用——遞迴
遞迴函式:我們把一個直接呼叫自己或者通過一系列的呼叫語言間接地呼叫自己的函式,叫做遞迴函式。
當然,寫程式最怕陷入無限迴圈,所以,每個遞迴定義必須至少有一個條件,滿足時遞迴不再進行,即不再引用自身而是返回值退出。
那麼遞迴和棧有什麼關係呢?
遞迴過程退回的順序是它前進順序的逆序。在退回過程中,可能要執行某些動作,包括恢復在前行過程中儲存起來的某些資料。
簡單地說,就是在前行階段,對於每一層的遞迴,函式的區域性變數、引數值以及返回地址都會被壓入棧中。在退回階段,位於棧頂的佈局變數、引數值和返回地址被彈出,用於返回撥用層次中執行程式碼的剩餘部分,也就是恢復了呼叫的狀態。
簡而言之,遞迴過程的退回順序就是前進順序的逆序,所以使用棧是最好的資料結構。
獲取更多精彩內容,關注我的微信公眾號——Android機動車!