後進者先出,先進者後出,這就是典型的棧的結構。在生活中刷碗的時候,一摞摞盤子,小時候的玩具槍都有點類似棧這個結構。從棧的操作特性上來看,棧是一種「操作受限」的線性表,只允許在一端插入和刪除資料。
有時也有所困惑,陣列和鍊錶的結構已經非常方便快捷了,為什麼還需要棧這個結構呢?棧是為當某個資料集合只涉及在一端插入和刪除資料,而抽象存在的。
棧既可以用陣列來實現,也可以用鍊錶來實現。用陣列實現的棧,我們叫作順序棧,用鍊錶實現的棧,我們叫作鏈式棧。
不管是順序棧還是鏈式棧,我們儲存資料只需要乙個大小為 n 的陣列就夠了。在入棧和出棧過程中,只需要一兩個臨時變數儲存空間,所以空間複雜度是 o(1)。
如果要實現乙個支援動態擴容的棧,我們只需要底層依賴乙個支援動態擴容的陣列就可以了。當棧滿了之後,我們就申請乙個更大的陣列,將原來的資料搬移到新陣列中。
對於出棧操作來說,我們不會涉及記憶體的重新申請和資料的搬移,所以出棧的時間複雜度仍然是 o(1)。但是,對於入棧操作來說,情況就不一樣了。當棧中有空閒空間時,入棧操作的時間複雜度為 o(1)。但當空間不夠時,就需要重新申請記憶體和資料搬移,所以時間複雜度就變成了 o(n)。
對列也是一種特別常用的資料結構,佇列跟棧一樣,也是一種操作受限的線性表資料結構,兩個常用的操作入隊和出隊,我們常會想起redis中的list結構中的l/rpop和l/rpush方法。
用陣列實現的佇列叫作順序佇列,用鍊錶實現的佇列叫作鏈式佇列。
對於棧來說,我們只需要乙個棧頂指標就可以了。但是佇列需要兩個指標:乙個是 start 指標,指向隊頭;乙個是 end 指標,指向隊尾。
隨著不停地進行入隊、出隊操作,start 和 end 都會持續往後移動。當 end 移動到最右邊,即使陣列中還有空閒空間,也無法繼續往佇列中新增資料了,在原定義的佇列裡當空間用盡,統一挪動到head位置。
<?php
class
deque
/**(尾部)出隊**/
public
function
removelast()
/**(頭部)入隊**/
public
function
addfirst
($value
)/**(頭部)出隊**/
public
function
removefirst()
/**清空佇列**/
public
function
makeempty()
/**獲取列頭**/
public
function
getfirst()
/** 獲取列尾 **/
public
function
getlast()
/** 獲取長度 **/
public
function
getlength()
}
秒殺場景中,我們把預定商品的使用者請求,放到預存的佇列中,在定時任務中進行消費,實現了乙個(生產-消費)的佇列使用。
就是在隊列為空的時候,從隊頭取資料會被阻塞。因為此時還沒有資料可取,直到佇列中有了資料才能返回;如果佇列已經滿了,那麼插入資料的操作就會被阻塞,直到佇列中有空閒位置後再插入資料,然後再返回。可以有效地協調生產和消費的速度。
執行緒安全的佇列我們叫作併發佇列。最簡單直接的實現方式是直接在 enqueue()、dequeue() 方法上加鎖,但是鎖粒度大併發度會比較低,同一時刻僅允許乙個存或者取操作。實際上,基於陣列的迴圈佇列,利用 cas 原子操作,可以實現非常高效的併發佇列。這也是迴圈佇列比鏈式佇列應用更加廣泛的原因。
資料結構 棧與佇列
題目 1.編寫函式,採用鏈式儲存實現棧的初始化 入棧 出棧操作 2.編寫函式,採用順序儲存實現棧的初始化 入棧 出棧操作 3.編寫函式,採用鏈式儲存實現佇列的初始化 入隊 出隊操作 4.編寫函式,採用順序儲存實現佇列的初始化 入隊 出隊操作 5.編寫乙個主函式,在主函式中設計乙個簡單的選單,分別除錯...
資料結構 棧與佇列
棧的原則是後進先出,即插入與刪除元素均在棧頂進行。獲取棧頂元素 s.top 佇列的原則是先進先出,即插入資料在隊尾進行,刪除資料在隊頭進行。獲取隊頭元素 q.front 思路 用兩個棧,乙個棧用來進隊,乙個棧用來出隊,當資料進入佇列的時候,我們將其壓入乙個棧,當資料出隊的時候,我們將儲存在棧內的資料...
資料結構 棧與佇列
1.順序棧 基本操作 typedef int elemtype 定義 順序棧 typedef struct sqstack 判空 bool stackempty sqstack s 進棧 bool push sqstack s elemtype x 出棧操作 bool pop sqstack s e...