棧(stack)是限制插入和刪除只能在乙個位置上進行的表,該位置是表的末端,叫做棧頂(top)。它是後進先出(lifo)的。對棧的基本操作只有push(進棧)和pop(出棧)兩種,前者相當於插入,後者相當於刪除最後的元素。
實現:利用乙個單鏈表來實現棧的資料結構。而且,因為我們都只針對棧頂元素進行操作,所以借用單鏈表的頭就能讓所有棧的操作在 o(1) 的時間內完成。
如果打算用乙個陣列外加乙個指標來實現相似的效果,那麼,一旦陣列的長度發生了改變,哪怕只是在最後新增乙個新的元素,時間複雜度都不再是 o(1),而且,空間複雜度也得不到優化。
注意:棧是許多 leetcode 中等難度偏上的題目裡面經常需要用到的資料結構,掌握好它是十分必要的。
leetcode 第 739 題:根據每日氣溫列表,請重新生成乙個列表,對應位置的輸入是你需要再等待多久溫度才會公升高超過該日的天數。如果之後都不會公升高,請在該位置用 0 來代替。
第乙個溫度值是 23 攝氏度,它要經過 1 天才能等到溫度的公升高,也就是在第二天的時候,溫度公升高到 24 攝氏度,所以對應的結果是 1。接下來,從 25 度到下一次溫度的公升高需要等待 4 天的時間,那時溫度會變為 26 度。
思路 1:最直觀的做法就是針對每個溫度值向後進行依次搜尋,找到比當前溫度更高的值,這樣的計算複雜度就是 o(n2)。
但是,在這樣的搜尋過程中,產生了很多重複的對比。例如,從 25 度開始往後面尋找乙個比 25 度更高的溫度的過程中,經歷了 21 度、19 度和 22 度,而這是乙個溫度由低到高的過程,也就是說在這個過程中已經找到了 19 度以及 21 度的答案,它就是 22 度。
思路 2:可以運用乙個堆疊 stack 來快速地知道需要經過多少天就能等到溫度公升高。從頭到尾掃瞄一遍給定的陣列 t,如果當天的溫度比堆疊 stack 頂端所記錄的那天溫度還要高,那麼就能得到結果。
對第乙個溫度 23 度,堆疊為空,把它的下標壓入堆疊;
下乙個溫度 24 度,高於 23 度高,因此 23 度溫度公升高只需 1 天時間,把 23 度下標從堆疊裡彈出,把 24 度下標壓入;
同樣,從 24 度只需要 1 天時間公升高到 25 度;
21 度低於 25 度,直接把 21 度下標壓入堆疊;
19 度低於 21 度,壓入堆疊;
22 度高於 19 度,從 19 度公升溫只需 1 天,從 21 度公升溫需要 2 天;
由於堆疊裡儲存的是下標,能很快計算天數;
22 度低於 25 度,意味著尚未找到 25 度之後的公升溫,直接把 22 度下標壓入堆疊頂端;
後面的溫度與此同理。
該方法只需要對陣列進行一次遍歷,每個元素最多被壓入和彈出堆疊一次,演算法複雜度是 o(n)。
佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,和棧一樣,佇列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。
實現:可以借助雙鏈表來實現佇列。雙鏈表的頭指標允許在隊頭檢視和刪除資料,而雙鏈表的尾指標允許我們在隊尾檢視和新增資料。在演算法面試題當中,廣度優先搜尋(breadth-first search)是運用佇列最多的地方。
特點:雙端佇列和普通佇列最大的不同在於,它允許我們在佇列的頭尾兩端都能在 o(1) 的時間內進行資料的檢視、新增和刪除。
應用場景:雙端佇列最常用的地方就是實現乙個長度動態變化的視窗或者連續區間,而動態視窗這種資料結構在很多題目裡都有運用。
leetcode 第 239 題:給定乙個陣列 nums,有乙個大小為 k 的滑動視窗從陣列的最左側移動到陣列的最右側。你只可以看到在滑動視窗 k 內的數字,滑動視窗每次只向右移動一位。返回滑動視窗最大值。
注意:你可以假設 k 總是有效的,1 ≤ k ≤ 輸入陣列的大小,且輸入陣列不為空。
示例:給定乙個陣列以及乙個視窗的長度 k,現在移動這個視窗,要求列印出乙個陣列,陣列裡的每個元素是當前視窗當中最大的那個數。
輸入:nums = [1, 3, -1, -3, 5, 3, 6, 7],k = 3
輸出:[3, 3, 5, 5, 6, 7]
思路 1:移動視窗,掃瞄,獲得最大值。假設陣列裡有 n 個元素,演算法複雜度就是 o(n)。這是最直觀的做法。
思路 2:利用乙個雙端佇列來儲存當前視窗中最大那個數在陣列裡的下標,雙端佇列新的頭就是當前視窗中最大的那個數。通過該下標,可以很快地知道新的視窗是否仍包含原來那個最大的數。如果不再包含,我們就把舊的數從雙端佇列的頭刪除。
因為雙端佇列能讓上面的這兩種操作都能在 o(1) 的時間裡完成,所以整個演算法的複雜度能控制在 o(n)。
初始化視窗 k=3,包含 1,3,-1,把 1 的下標壓入雙端佇列的尾部;
把 3 和雙端佇列的隊尾的資料逐個比較,3 >1,把 1 的下標彈出,把 3 的下標壓入隊尾;
-1<3,-1 壓入雙端佇列隊尾保留到下一視窗進行比較;
3 為當前視窗的最大值;
視窗移動,-3 與隊尾資料逐個比較,-3
3 為當前視窗的最大值;
視窗繼續移動,5>-3,-3 從雙端佇列隊尾彈出;
5>-1,-1 從隊尾彈出;
3 超出當前視窗,從佇列頭部彈出;
5 壓入佇列頭部,成為當前視窗最大值;
繼續移動視窗,操作與上述同理。
視窗最大值只需讀取雙端佇列頭部元素。
資料結構 棧與佇列
題目 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...