栈与队列-
基礎
stl中棧和隊列不是容器,而是容器適配器。它們提供接口,由其他容器實現。
20. 有效的括號
給定一個只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判斷字符串是否有效。
有效字符串需滿足:
左括號必須用相同類型的右括號閉合。
左括號必須以正確的順序閉合。
示例 1:
輸入:s = “()” 輸出:true
示例 2:
輸入:s = “()[]{}” 輸出:true
示例 3:
輸入:s = “(]” 輸出:false
示例 4:
輸入:s = “([)]” 輸出:false
示例 5:
輸入:s = “{[]}” 輸出:true
第一種情況:已經遍歷完了字符串,但是棧不為空,說明有相應的左括號沒有右括號來匹配,所以return false
第二種情況:遍歷字符串匹配的過程中,發現棧里沒有要匹配的字符。所以return false
第三種情況:遍歷字符串匹配的過程中,棧已經為空了,沒有匹配的字符了,說明右括號沒有找到對應的左括號return false
那么什么時候說明左括號和右括號全都匹配了呢,就是字符串遍歷完之后,棧是空的,就說明全都匹配了。
tips:在匹配左括號的時候,右括號先入棧,就只需要比較當前元素和棧頂相不相等就可以了,比左括號先入棧代碼實現要簡單的多了!
232. 用棧實現隊列
請你僅使用兩個棧實現先入先出隊列。隊列應當支持一般隊列支持的所有操作(push、pop、peek、empty):
實現 MyQueue 類:
void push(int x) 將元素 x 推到隊列的末尾
int pop() 從隊列的開頭移除并返回元素
int peek() 返回隊列開頭的元素
boolean empty() 如果隊列為空,返回 true ;否則,返回 false
說明:
你 只能 使用標準的棧操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的語言也許不支持棧。你可以使用 list 或者 deque(雙端隊列)來模擬一個棧,只要是標準的棧操作即可。
示例 1:
輸入: [“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”] [[], [1], [2],
[], [], []]
輸出: [null, null, null, 1, 1, false]
解釋: MyQueue myQueue = new MyQueue(); myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1 myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
在push數據的時候,只要數據放進輸入棧就好,但在pop的時候,操作就復雜一些,輸出棧如果為空,就把進棧數據全部導入進來(注意是全部導入),再從出棧彈出數據,如果輸出棧不為空,則直接從出棧彈出數據就可以了。
最后如何判斷隊列為空呢?如果進棧和出棧都為空的話,說明模擬的隊列為空了。
class MyQueue:def __init__(self):"""in主要負責push,out主要負責pop"""self.stack_in = []self.stack_out = []def push(self, x: int) -> None:# 有新元素就進來 放在inself.stack_in.append(x)def pop(self) -> int:if self.empty():return None# 如果出棧不空 直接pop 如果為空就先將入棧的所有元素移動到出棧if self.stack_out:return self.stack_out.pop()else:for i in range(len(self.stack_in)):self.stack_out.append(self.stack_in.pop())return self.stack_out.pop()def peek(self) -> int:# 只需要返回第一個元素 所有pop后還要加回來ans=self.pop()self.stack_out.append(ans)return ansdef empty(self) -> bool:"""只要in或者out有元素,說明隊列不為空"""return not(self.stack_in or self.stack_out)# Your MyQueue object will be instantiated and called as such: # obj = MyQueue() # obj.push(x) # param_2 = obj.pop() # param_3 = obj.peek() # param_4 = obj.empty()225. 用隊列實現棧
請你僅使用兩個隊列實現一個后入先出(LIFO)的棧,并支持普通棧的全部四種操作(push、top、pop 和 empty)。
實現 MyStack 類:
void push(int x) 將元素 x 壓入棧頂。
int pop() 移除并返回棧頂元素。
int top() 返回棧頂元素。
boolean empty() 如果棧是空的,返回 true ;否則,返回 false 。
注意:
你只能使用隊列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 這些操作。
你所使用的語言也許不支持隊列。 你可以使用 list (列表)或者 deque(雙端隊列)來模擬一個隊列 , 只要是標準的隊列操作即可。
示例:
輸入: [“MyStack”, “push”, “push”, “top”, “pop”, “empty”] [[], [1], [2],
[], [], []] 輸出: [null, null, null, 2, 2, false]
解釋: MyStack myStack = new MyStack(); myStack.push(1); myStack.push(2);
myStack.top(); // 返回 2 myStack.pop(); // 返回 2 myStack.empty(); // 返回
False
用兩個隊列que1和que2實現隊列(先進先出)的功能,que2其實完全就是一個備份的作用,把que1最后面的元素以外的元素都備份到que2,然后彈出最后面的元素,再把其他元素從que2導回que1。
優化
其實這道題目就是用一個隊列就夠了。
一個隊列在模擬棧彈出元素的時候只要將隊列頭部的元素(除了最后一個元素外) 重新添加到隊列尾部,此時在去彈出元素就是棧的順序了。
class MyStack:def __init__(self):"""Python普通的Queue或SimpleQueue沒有類似于peek的功能也無法用索引訪問,在實現top的時候較為困難。用list可以,但是在使用pop(0)的時候時間復雜度為O(n)因此這里使用雙向隊列,我們保證只執行popleft()和append(),因為deque可以用索引訪問,可以實現和peek相似的功能in - 存所有數據out - 僅在pop的時候會用到"""self.queue_in =deque()self.queue_out = deque()def push(self, x: int) -> None:self.queue_in.append(x)def pop(self) -> int:"""1. 首先確認不空2. 因為隊列的特殊性,FIFO,所以我們只有在pop()的時候才會使用queue_out3. 先把queue_in中的所有元素(除了最后一個),依次出列放進queue_out4. 交換in和out,此時out里只有一個元素5. 把out中的pop出來,即是原隊列的最后一個tip:這不能像棧實現隊列一樣,因為另一個queue也是FIFO,如果執行pop()它不能像stack一樣從另一個pop(),所以干脆in只用來存數據,pop()的時候兩個進行交換例子:old_in: 1 2 3 4out: 1 2 3new_in: 4交換out 和 new_in然后彈出4 實現后進先出"""if self.empty():return Nonefor i in range(len(self.queue_in)-1):self.queue_out.append(self.queue_in.popleft())# 交換in和outself.queue_in,self.queue_out = self.queue_out,self.queue_inreturn self.queue_out.popleft()def top(self) -> int:"""1. 首先確認不空2. 我們僅有in會存放數據,所以返回第一個即可"""if self.empty():return Nonereturn self.queue_in[-1]def empty(self) -> bool:"""因為只有in存了數據,只要判斷in是不是有數即可"""return len(self.queue_in) == 0# Your MyStack object will be instantiated and called as such: # obj = MyStack() # obj.push(x) # param_2 = obj.pop() # param_3 = obj.top() # param_4 = obj.empty()1047. 刪除字符串中的所有相鄰重復項
給出由小寫字母組成的字符串 S,重復項刪除操作會選擇兩個相鄰且相同的字母,并刪除它們。
在 S 上反復執行重復項刪除操作,直到無法繼續刪除。
在完成所有重復項刪除操作后返回最終的字符串。答案保證唯一。
示例:
輸入:“abbaca” 輸出:“ca” 解釋: 例如,在 “abbaca” 中,我們可以刪除 “bb”
由于兩字母相鄰且相同,這是此時唯一可以執行刪除操作的重復項。之后我們得到字符串 “aaca”,其中又只有 “aa”
可以執行重復項刪除操作,所以最后的字符串為 “ca”。
150. 逆波蘭表達式求值
根據 逆波蘭表示法,求表達式的值。
有效的算符包括 +、-、*、/ 。每個運算對象可以是整數,也可以是另一個逆波蘭表達式。
注意 兩個整數之間的除法只保留整數部分。
可以保證給定的逆波蘭表達式總是有效的。換句話說,表達式總會得出有效數值且不存在除數為 0 的情況。
示例 1:
輸入:tokens = [“2”,“1”,“+”,“3”,“*”]
輸出:9
解釋:該算式轉化為常見的中綴算術表達式為:((2 + 1) * 3) = 9
示例 2:
輸入:tokens = [“4”,“13”,“5”,“/”,“+”]
輸出:6
解釋:該算式轉化為常見的中綴算術表達式為:(4 + (13 / 5)) = 6
示例 3:
輸入:tokens = [“10”,“6”,“9”,“3”,“+”,“-11”,““,”/“,””,“17”,“+”,“5”,“+”]
輸出:22
解釋:該算式轉化為常見的中綴算術表達式為:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
相鄰字符串消除的過程,和1047.刪除字符串中的所有相鄰重復項 (opens new window)中的對對碰游戲是不是就非常像了。
239. 滑動窗口最大值
給你一個整數數組 nums,有一個大小為 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字。滑動窗口每次只向右移動一位。
返回 滑動窗口中的最大值 。
示例 1:
輸入:nums = [1,3,-1,-3,5,3,6,7], k = 3
輸出:[3,3,5,5,6,7]
解釋:
滑動窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
輸入:nums = [1], k = 1 輸出:[1]
思路
在上述滑動窗口形成及移動的過程中,我們注意到元素是從窗口的右側進入的,然后由于窗口大小是固定的,因此多余的元素是從窗口左側移除的。 一端進入,另一端移除,這不就是隊列的性質嗎?所以,該題目可以借助隊列來求解。
-
遍歷給定數組中的元素,如果隊列不為空且當前考察元素大于等于隊尾元素,則將隊尾元素移除。直到,隊列為空或當前考察元素小于新的隊尾元素;
-
當隊首元素的下標小于滑動窗口左側邊界left時,表示隊首元素已經不再滑動窗口內,因此將其從隊首移除。
-
由于數組下標從0開始,因此當窗口右邊界right+1大于等于窗口大小k時,意味著窗口形成。此時,隊首元素就是該窗口內的最大值
題解參考:
class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:left,right = 0,0n = len(nums)# 左頭右尾deque=collections.deque()while right<k:if deque and deque[-1] < nums[right]:deque.pop()else:deque.append(nums[right])right += 1#窗口設置完成 dequeue為[ 3, -1]res = []res.append(deque[0]) while right < n:# 如果隊列不空且當前元素大于隊尾元素 將隊尾元素移除 直到保持隊列內遞減次序# 即把隊列內小于當前元素的值移除while deque and nums[right]>deque[-1]:deque.pop()# 加入當前元素deque.append(nums[right])# 判斷隊首元素下標是否 < left, 是說明隊首不在窗口內,移除隊首 隊首元素是否在窗口中# 在python判斷不了下標 所以等價于 如果left指向的值與隊首元素相同 下一步left++ 則這個max就需要移除 不被包含在隊列中#例如 1 3-1 -3 5 deque=[3 -1 -3] 此時left==1 3是max彈出if nums[left] == deque[0]:deque.popleft()left+=1res.append(deque[0])right +=1return res class Solution:def maxSlidingWindow(self, nums,k) :queue=[] #記錄的是元素的索引值,對應的元素按照遞減順序ans=[]for index,value in enumerate(nums):print(index,value,nums,queue)#移除不在窗口中的元素 隊首元素下標在left左邊 說明不在窗口left = index-k+1while queue and queue[0]<left:queue.pop(0)#添加新元素 如果當前元素比隊尾元素大 則移除隊尾元素 直到滿足隊列元素遞減while queue and nums[queue[-1]]<=value:queue.pop()queue.append(index)#選擇最大值ans.append(nums[queue[0]])#ans [1, 3, 3, 3, 5, 5, 6, 7] 去除ansreturn ans[k-1:]347.前 K 個高頻元素
給定一個非空的整數數組,返回其中出現頻率前 k 高的元素。
示例 1:
輸入: nums = [1,1,1,2,2,3], k = 2 輸出: [1,2]
示例 2:
輸入: nums = [1], k = 1 輸出: [1]
法一:用庫
class Solution:def topKFrequent(self, nums: List[int], k: int) -> List[int]:c = collections.Counter(nums)# Counter({1: 3, 2: 2, 3: 1})c = c.most_common(k)#C [(1, 3), (2, 2)]res=[]for x in c:# 添加keyres.append(x[0])return res法二 優先隊列
參考https://www.programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html
- 要統計元素出現頻率
- 對頻率排序
- 找出前K個高頻元素
因為優先級隊列對外接口只是從隊頭取元素,從隊尾添加元素,再無其他取元素的方式,看起來就是一個隊列。
而且優先級隊列內部元素是自動依照元素的權值排列。那么它是如何有序排列的呢?
缺省情況下priority_queue利用max-heap(大頂堆)完成對元素的排序,這個大頂堆是以vector為表現形式的complete binary tree(完全二叉樹)。
如果定義一個大小為k的大頂堆,在每次移動更新大頂堆的時候,每次彈出都把最大的元素彈出去了,那么怎么保留下來前K個高頻元素呢。
我們要用小頂堆,因為要統計最大前k個元素,只有小頂堆每次將最小的元素彈出,最后小頂堆里積累的才是前k個最大元素。
class Solution(object):def topKFrequent(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""count = {}for x in nums:if x not in count:count[x] = 1else:count[x] += 1# 小頂堆 從小到大排序pri_que = []for key, freq in count.items():# 帶權值排序 即按freq排序但帶著keyheapq.heappush(pri_que, (freq, key))print(pri_que)if len(pri_que) > k:heapq.heappop(pri_que)result = [0] * k# 倒序輸出 輸出keyfor i in range(k - 1, -1, -1):print()result[i] = heapq.heappop(pri_que)[1]return result總結
- 上一篇: 指定gpu训练
- 下一篇: screen常用命令以及screen恢复