棧 Stack
棧又稱堆棧,是一種運算受限的線性表,其限制是僅允許在表的一端進行插入和刪除運算。
把對棧進行運算的一端稱為棧頂,另一端稱為棧底。
向一個棧插入新元素稱為入棧或進棧,Push;從一個棧刪除元素稱為退棧或出棧,Pop。
因為后進棧的元素必定先出棧,所以又把棧稱為后進先出表(Last In First Out, LIFO)。
棧的順序存儲結構
棧的順序存儲結構需要使用一個數組和一個整型變量來實現。利用數組來順序存儲棧中的所有元素,利用整型變量來存儲棧頂元素的下標位置,可這個變量稱為棧頂指針。
可以定義如下:
const int MaxSize = 50; struct Stack { ElemType stack[MaxSize]; int top; };
若要對存儲棧的數組空間采用動態分配,則可定義如下:
struct Stack { ElemType *stack; int top; int MaxSize; };
top的值為-1表示棧空。
棧的鏈式存儲結構
棧的鏈式存儲結構是通過由結點構成的單鏈表實現的,此時表頭指針被稱為棧頂指針,由棧頂指針指向的表頭結點被稱為棧頂結點,整個單鏈表被稱為鏈棧。
對鏈棧的插入和刪除操作是在單鏈表的表頭進行的。
當向一個鏈棧插入元素時,是把該元素插入到棧頂,即,使該元素結點的指針域指向原來的棧頂結點,而棧頂指針則修改為指向該元素結點,使該結點成為新的棧頂結點。
棧的應用
簡單應用
1.輸入,之后逆序輸出。
2.語法檢查:括號匹配。
每當掃描到大中小的左括號后,令其進棧,當掃描到右括號時,則檢查棧頂是否為相應的左括號,若是則退棧處理,若不是則出現了語法錯誤。當掃描到文件結尾,若棧為空則表明沒有發現括號配對錯誤。
3.數制轉換:把十進制的整數轉換為二至九之間的任一進制數輸出。
轉換方法:要轉換為r進制,則原來的數逐次除以基數r(除完之后用商再除),直到商為0,得到的一系列余數的逆序就是轉換結果。
算術表達式的計算
在中綴表達式(就是我們人類通常寫的算術表達式)中,計算需要注意優先級、括號這些問題,和運算符的實際運算次序往往同它們在表達式中的先后次序不一致,所以波蘭科學家提出了后綴表達式,把運算符放在兩個運算對象的后面。
在后綴表達式中看,不存在括號,也不存在運算符優先級的差別,計算過程完全按照運算符出現的先后次序進行,整個計算過程僅需掃描一遍便可完成。
中綴表達式轉換成后綴表達式:
中綴算術表達式轉換成對應的后綴算術表達式的規則是:把每個運算符都移到它的兩個運算對象的后面,然后刪除掉所有的括號即可。
為了轉換正確,必須設定一個運算符棧,並在棧底放入一個特殊算符,假定為@,讓它具有最低的運算符優先級,此棧用來保存掃描中綴表達式得到的暫不能放入后綴表達式中的運算符,待它的兩個運算對象都放入到后綴表達式之后,再令其出棧並寫入到后綴表達式中。
轉換過程如下:從頭到尾掃描中綴表達式,若遇到數字則直接寫入后綴表達式,若遇到運算符,則比較棧頂元素和該運算符的優先級,當該運算符的優先級大於棧頂元素的時候,表明該運算符的后一個運算對象還沒有進入后綴表達式,應該把該運算符暫存於運算符棧中,然后把它的后一個運算對象寫入到后綴表達式中,再令其出棧並寫入后綴表達式中;若遇到的運算符優先級小於等於棧頂元素的優先級,表明棧頂運算符的兩個運算對象已經被寫入后綴表達式,應將棧頂元素出棧並寫入后綴表達式,對於新的棧頂元素仍進行比較和處理,直到棧頂元素的優先級小於當前等待處理的運算符的優先級為止,然后令該運算符進棧即可。
按照上述過程掃描到中綴表達式的末尾,把剩余的運算符依次出棧並寫入后綴表達式即可。
(對於左括號直接進棧,右括號則使左右兩個括號內的運算符都出棧)。
后綴表達式求值
后綴表達式求值也需要一個棧,其元素類型為操作數的類型,此棧存儲后綴表達式中的操作數、計算過程的中間結果及最后結果。
計算過程如下:掃描后綴表達式,若遇到操作數則進棧,若遇到操作符則彈出兩個操作數進行計算,然后將結果壓進棧,直到最后掃描完畢,棧中應該保存着最終結果。
棧與遞歸
在計算機系統內,執行遞歸函數是通過自動使用棧來實現的,棧中的每個元素包含有遞歸函數的參數域、每個局部變量域和調用后的返回地址域,其中引用參數域只保存傳送來的實參的地址。每次進行函數調用,都把相應的值壓入棧,每次調用結束,都按照本次返回地址返回到指定位置執行,並且自動做一次退棧操作。
關於遞歸的應用就又是一個大話題了,此處就不多說了。