第四章 棧與隊列,第四章隊列
一、棧的定義
棧(stack)是限定盡在表尾進行插入和刪除操作的線性表。
我們把允許插入和刪除的一端成為棧頂(top),另一端成為棧底(bottom),不含任何資料元素的棧稱為空白棧。棧又稱為後進先出(LIFO)的線性表。
圖示出棧入棧操作:
二、棧的抽象資料類型
圖示棧的各項操作:
由於棧本身就是一個線性表,那麼上一章我們討論了線性表的順序儲存和鏈式儲存,對於棧來說也是同樣適用的。
三、棧的順序儲存結構及實現
來看一下棧的結構定義:
若儲存棧的長度為StackSize,則棧頂位置top必須小於StackSize。當棧存在一個元素時,top等於0,因此常把空棧的判定條件定位top等於-1。
看一下:
下面看一下push操作的代碼:
出棧pop,代碼如下:
兩者都沒有涉及到任何迴圈語句,因此時間複雜度均為O(1)。
四、棧的鏈式儲存結構及實現
棧的鏈式儲存結構,簡稱鏈棧。
鏈棧的結構代碼如下:
進棧操作:
出棧操作:
上述操作也不包含迴圈,因此時間複雜度都是O(1)。
對於順序棧和鏈棧的選擇:如果棧的使用過程中元素變化不可預料,有時很小,有時非常大,那麼最好用鏈棧,反之,如果它的變化在可控範圍內,建議使用順序棧會更好些。
五、棧的應用-遞迴
遞迴的最經典例子:(斐波那契額數列)
代碼如下:
遞迴的定義:我們把一個直接調用自己或通過一系列的調用語句間接地調用自己得函數,稱為遞迴函式。
遞迴程式最可怕的就是陷入永不結束的無窮遞迴中,所以,每個遞迴定義必須至少有一個條件,滿足時遞迴不再進行,即不再引用自身而是返回值退出。
六、棧的應用-四則運算運算式求值
我們介紹一種不需要括弧的尾碼表達法,我們把它稱為逆波蘭(RPN)。
我們來看一個例子:對於運算:9+(3-1)*3+10/2,其逆波蘭運算式為:9 3 1 - 3 * 10 2 / + 這種運算式是反人類的運算式,但是是順電腦的,我們也就忍忍吧!
計算規則:從左至右遍曆運算式的每個數字和符號,遇到數字就進棧,遇到符號就將處於棧頂的兩個數字出棧,進行計算,將計算結果再入棧,一直重複直到擷取結果。
七、隊列的定義
定義:隊列是只允許在一段進行插入操作,而在另一端進行刪除操作的線性表。
隊列是一種先進先出(FIFO)的線性表。
隊列的抽象資料類型:
迴圈隊列的定義:我們把隊列的這種頭尾相連的順序儲存結構稱為迴圈隊列。
判斷隊列滿的條件是:(rear+1)%QueueSize==front
計算隊列長度的公式:(rear-front+QueueSize)%QueueSize
迴圈隊列的順序儲存結構代碼如下:
迴圈隊列的初始化代碼如下:
迴圈隊列求隊列長度代碼如下:
迴圈隊列的入隊作業碼如下:
迴圈隊列的出隊作業碼如下:
迴圈隊列面臨著數組可能會溢出的問題,我們還要研究一下不用擔心隊列長度的鏈式儲存結構。
八、隊列的鏈式儲存結構及實現
隊列的鏈式儲存結構,其實就是線性表的單鏈表,只不過它只能尾進頭出而已,我們把它簡稱為鏈隊列。
鏈隊列的結構為:
隊列鏈式儲存結構--入隊操作
代碼如下:
隊列鏈式儲存結構--出隊操作
代碼如下:
總的來說,在可以確定隊列長度最大值的情況下,建議用迴圈隊列,如果你無法預估隊列的長度時,則用鏈隊列。
九、總結回顧
棧是限定僅在表尾進行插入和刪除操作的線性表。
隊列是只允許在一段進行插入操作,而在另一端進行刪除操作的線性表。
它們均可以使用線性表的順序儲存結構來實現,但都存在著順序儲存的一些弊端。
對於隊列來說,為了避免數組插入和刪除時需要移動資料,於是就引入了迴圈隊列,使得隊頭和隊尾可以在數組中迴圈變化。解決了移動資料的時間損耗,使得本來插入和刪除是O(n)的時間複雜度編程了O(1)。