日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

栈与队列-

發布時間:2024/4/13 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 栈与队列- 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基礎

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:在匹配左括號的時候,右括號先入棧,就只需要比較當前元素和棧頂相不相等就可以了,比左括號先入棧代碼實現要簡單的多了!

class Solution:def isValid(self, s: str) -> bool:stack = []for x in s:if x == '(':stack.append(')')elif x == '[':stack.append(']') elif x == '{':stack.append('}') # 當前元素與棧頂元素不符合 或者 棧已空s卻沒遍歷完 說明多了右括號 # 先要判斷棧空不空 空了再去判斷棧頂元素滿不滿足 否則棧空時 -1會越界elif not stack or x!=stack[-1]:return False# 如果遇到右括號 彈出棧頂元素else:stack.pop()# 遍歷完后 如果棧為空成功 棧不空失敗if stack==[]:return Trueelse:return False

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”。

class Solution:def removeDuplicates(self, s: str) -> str:res = list()# 相當于括號匹配'''"abbaca"遇到ab 添加 res=[a,b]遇到第二個b時候 pop 彈出b'''for x in s:# 先確保棧不空 才有-1if res and res[-1] == x:res.pop()else:res.append(x)return ''.join(res)# 字符串拼接

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)中的對對碰游戲是不是就非常像了。

class Solution:def evalRPN(self, tokens: List[str]) -> int:res=[]for i in range(len(tokens)):if tokens[i] =='+' or tokens[i] =='-' or tokens[i] =='*' or tokens[i] =='/' :num1=int(res.pop())num2=int(res.pop())if tokens[i] =='+':res.append(num1+num2)if tokens[i] =='-':res.append(num2-num1)if tokens[i] =='*':res.append(num1*num2)if tokens[i] =='/':res.append(int(num2 / num1))else:# 添加數字res.append(tokens[i])return int(res[-1])

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

總結

以上是生活随笔為你收集整理的栈与队列-的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。