栈一种先进后出,队列先进后出。
Python中可以用list实现栈,用append()模拟入栈,用pop()模拟出栈。
也可以用list实现队列,但是效率较低,一般用collections.deque模拟(双端)队列。
5. 数据结构 — Python 3.11.5 文档
使用list进行栈的操作
stack = [3, 4, 5]
stack.append(6)
stack.append(7)
stack
[3, 4, 5, 6, 7]
stack.pop()
7
stack
[3, 4, 5, 6]
使用collections.dequez进行队列操作
from collections import deque
queue = deque(["Eric", "John", "Michael"])
queue.append("Terry") # Terry arrives
queue.append("Graham") # Graham arrives
queue.popleft() # The first to arrive now leaves
'Eric'
queue.popleft() # The second to arrive now leaves
'John'
queue # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])
#模拟
请你仅使用两个栈实现先入先出队列。
思路:
使用两个栈stackin和stackout分别进行入队和出队。
出队时,如果stackout为空,将stackin 倒入 stackout。
class MyQueue:
def __init__(self):
self.stackin = []
self.stackout = []
def push(self, x: int) -> None:
self.stackin.append(x)
def pop(self) -> int:
if self.empty():
return None
if not self.stackout:
while self.stackin:
self.stackout.append(self.stackin.pop())
return self.stackout.pop()
def peek(self) -> int:
res = self.pop()
self.stackout.append(res)
return res
def empty(self) -> bool:
return not (self.stackin or self.stackout)
#模拟
重点还是pop。使用队列弹出元素时,需要先把之前的n-1个元素弹出,才能弹出第n个元素。
所以这里先弹出n-1个元素并添加到队列末尾,然后第n个元素就到了队列的开头。
这题没法仿照232的思路。注意队列和栈的区别,栈是反转元素进出顺序的,两次反转则变为先进先出。而队列是保持顺序的,进出两次队列后顺序不变。
class MyStack:
def __init__(self):
self.que = deque()
def push(self, x: int) -> None:
self.que.append(x)
def pop(self) -> int:
if self.empty():
return None
for i in range(len(self.que)-1):
self.que.append(self.que.popleft())
return self.que.popleft()
def top(self) -> int:
if self.empty():
return None
return self.que[-1]
def empty(self) -> bool:
return not self.que
#栈
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串的括号是否有效。
栈的应用,使用栈来存储左括号,遇到右括号则弹出左括号进行匹配。
class Solution:
def isValid(self, s: str) -> bool:
stack = []
d_k = {'(': ')', '{': '}', '[': ']'}
for c in s:
if c in d_k.keys():
stack.append(c)
else:
if len(stack) == 0:
return False
left = stack.pop()
if c != d_k[left]:
return False
return len(stack) == 0
删除字符串的相邻重复字母,可以重复多次,直到没有没有相邻重复项。
#模拟 #栈
class Solution:
def removeDuplicates(self, s: str) -> str:
stack = []
for c in s:
if len(stack) > 0 and stack[-1] == c:
stack.pop()
else:
stack.append(c)
return "".join(stack)
#栈
逆波兰表达式又叫后缀表达式,运算符在操作数的后面,如:1 2 +
我们一般写的是中缀表达式,运算符在操作数的中间,如 : 1 + 2
输入: tokens = [“2”,“1”,“+”,“3”,“*”]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
借助栈,比较容易计算后缀表达式,遇到操作数就入栈,遇到运算符就弹出前面两个数,然后计算,并将计算结果入栈。
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
res = 0
for i in tokens:
if i == '+' :
a = stack.pop()
b = stack.pop()
stack.append(b+a)
elif i == '-' :
a = stack.pop()
b = stack.pop()
stack.append(b-a)
elif i == '*' :
a = stack.pop()
b = stack.pop()
stack.append(b*a)
elif i == '/':
a = stack.pop()
b = stack.pop()
stack.append(int(b/a))
else :
stack.append(int(i))
return int(stack[-1])
看起来有点重复,可以简化一下:
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
res = 0
op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x / y)}
for i in tokens:
if i in op_map.keys():
a = stack.pop() # 后
b = stack.pop() # 前
stack.append(op_map[i](b,a)) # 注意顺序 b op a
else :
stack.append(int(i))
return int(stack[-1])
#单调队列 #队列
整体思路:希望有一个队列,在滑动窗口的时候,用push添加新的元素,用pop弹出被删除的元素,并且能直接获取队列的最大元素。
也就是说,我们希望实现一个队列存储可能成为窗口中最大值的元素。(称为单调队列)
self.queue = deque()
pop
掉,然后添加value。(也就意味着,队列中的元素都比新加入的value大。push操作很关键,保证了队列中的元素是单调递减的,因此后面可以用self.queue[0]
获取最大值)def push(self, value):
while self.queue and value > self.queue[-1]:# 队列末尾比value小则删除
self.queue.pop() # pop,弹出队列末尾元素
self.queue.append(value)
que[0]
,则弹出队首popleft()
。def pop(self, value):
if self.queue and value == self.queue[0]:
self.queue.popleft() # 弹出队首元素
获取最大值:
def front(self):
return self.queue[0]
使用单调队列来解决滑动窗口最大值就比较简单了,不断地调用pop和push和 front。
class MyQueue:
def __init__(self):
self.queue = deque()
# 删除value ,如果value 在队首则删除
def pop(self, value):
if self.queue and value == self.queue[0]:
self.queue.popleft()
# 添加value, valuea前面的比value小的都删除
def push(self, value):
while self.queue and value > self.queue[-1]:
self.queue.pop()
self.queue.append(value)
def front(self):
return self.queue[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
que = MyQueue()
res = []
for i in range(k):
que.push(nums[i])
res.append(que.front())
for i in range(k, len(nums)):
que.pop(nums[i-k])
que.push(nums[i])
res.append(que.front())
return res
#优先级队列 #堆
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。
最容易想到的是用字典统计频率然后排序。
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
mmap = Counter(nums)
sort = sorted(zip(mmap.values(),mmap.keys()), reverse=True)
res = []
for i in range(k):
res.append(sort[i][1])
return res
用堆排序,我们只需要维护一个大小为k的堆(优先级队列)。
在Python中可以用heapq或queue.PriorityQueue 实现。
heapq — 堆队列算法 — Python 3.11.5 文档
queue — 一个同步的队列类 — Python 3.11.5 文档
使用heapq
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
#要统计元素出现频率
map_ = Counter(nums)
#对频率排序
#定义一个小顶堆,大小为k
pri_que = [] #小顶堆
#用固定大小为k的小顶堆,扫描所有频率的数值
for key, freq in map_.items():
heapq.heappush(pri_que, (freq, key))
if len(pri_que) > k: #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
heapq.heappop(pri_que)
#找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
result = [0] * k
for i in range(k-1, -1, -1):
result[i] = heapq.heappop(pri_que)[1]
return result
使用PriorityQueue
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
#要统计元素出现频率
map_ = Counter(nums)
#对频率排序
from queue import PriorityQueue as PQ
pri_que = PQ(k+1) #优先级队列(相当于小根堆), 最多放k+1个元素
#用固定大小为k的小顶堆,扫描所有频率的数值
for key, freq in map_.items():
pri_que.put((freq, key))
if pri_que.qsize() > k:
pri_que.get()
print(pri_que.queue)
#找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
result = [0] * k
for i in range(k-1, -1, -1):
result[i] = pri_que.get()[1]
return result
栈主要用来处理相邻匹配问题,队列可以处理滑动窗口的最值问题(单调队列,前k个最值问题(优先级队列/小根堆)。
python中栈可以用list实现,队列用colelctions.deque实现。
stack = [3, 4, 5]
stack.append(6)
stack.append(7)
stack.pop()
import collections
queue = collections.deque()
queue.append(1) # 入队
queue.append(2)
queue.popleft() # 出队
此外还用到了优先级队列(堆),默认实现的是小根堆(堆顶元素最小)。
import heapq
pri_que = [] #小顶堆
heapq.heappush(pri_que, 1) # 入队
heapq.heappush(pri_que, 2)
heapq.heappop(pri_que) # 出队