二刷复习
栈和队列篇
文章目录
栈是一种先入后出的数据结构,以底层容器完成其所有的工作,提供push 和 pop 等接口,不提供走访功能,也不提供迭代器(iterator)不能遍历
栈是一种容器适配器,它可以用list实现也可以是deque实现
队列是先入先出的数据结构
在python中,from collections import deque,deque()是可以遍历的
from collections import deque
q = deque()
q.append('a')
q.append('b')
q.append('c')
for x in q:
print(x)
python中的deque()加元素是从右边加的,右边是队尾,popleft()是把元素从队头也就是队首pop出去;
记住右进左出
232.用栈实现队列(两个栈倒一下)
这个题的考点在于考队列和栈的区别
队列是先入先出,栈是先入后出;用栈模拟队列的关键点在于pop()函数怎么设计,就是说两个栈全空才是队列空,如果stack_out不空就直接pop,如果是空的话要从上一个栈pop给它加子弹
要记住两个栈就能模拟队列了;一个栈进元素一个栈作为出栈,这样来回倒一下就能实现先入先出了
class MyQueue:
def __init__(self):
self.stack_in = []
self.stack_out = []
def push(self, x: int) -> None:
self.stack_in.append(x)
def pop(self) -> int:
if self.empty(): return
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:
peek = self.pop()
self.stack_out.append(peek)
return peek
def empty(self) -> bool:
return not self.stack_in and not self.stack_out
225.用队列实现栈(把前n-1个元素加到队尾)
用队列实现栈
也是考察栈和队列的基本性质,也是主要实现在于pop()函数
思路在于把队列的前n-1个元素给pop()出来再加回到队尾
python中的deque()加元素是从右边加的,右边是队尾,popleft()是把元素从队头也就是队首pop出去;
记住右进左出
class MyStack:
def __init__(self):
self.queue = deque()
def push(self, x: int) -> None:
self.queue.append(x)
def pop(self) -> int:
if not self.queue: return
for i in range(len(self.queue)-1):
self.queue.append(self.queue.popleft())
return self.queue.popleft()
def top(self) -> int:
return self.queue[-1]
def empty(self) -> bool:
return not self.queue
删除字符串所有相邻重复项
删除字符串相邻重复项
这个没什么好说的
class Solution:
def removeDuplicates(self, s: str) -> str:
stack = []
for ch in s:
if stack and ch == stack[-1]: stack.pop()
else: stack.append(ch)
return ''.join(stack)
有效的括号
有效的括号
水平还是很菜,这道题看着案例想出把左括号加进栈,遇到对应的右括号等于栈顶的时候pop是很简单的思路;但是这道题不容易想的情况是右括号多或者右括号不匹配的情况;而代码中的else:就隐含了这两种情况
class Solution:
def isValid(self, s: str) -> bool:
a = {')':'(', '}':'{', ']':'[' }
stack = []
for ch in s:
if ch not in a.keys():
stack.append(ch)
elif stack and a[ch] == stack[-1]:
stack.pop()
else: return False #这个else很重要,隐含了两种情况,右括号多或者右括号不匹配
return not stack
后缀表达式求值(逆波兰表达式求值)
后缀表达式求值
这个题的思路是很简单的,遇到运算符的话从栈里掏出来两个数运算,但是要注意顺序因为栈的后入先出;
不要用isdigit(),有负数
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for ch in tokens:
if ch in ['+', '-', '*', '/']:
num1, num2 = int(stack.pop()), int(stack.pop())
if ch == "+": stack.append(num2+num1)
elif ch == "-": stack.append(num2-num1)
elif ch == "*": stack.append(num2*num1)
else: stack.append(num2/num1)
else: stack.append(ch)
return int(stack.pop())
239. 滑动窗口最大值(单调队列 O(n))
滑动窗口最大值
这个题的话需要用单调队列,单调队列的时间的复杂度是O(n)的;但是如果用滑窗加max()函数,时间复杂度是O(nlogk)会TLE
然后这个单调队列的话,是递减的,队首是大的,队尾是小的,关键还是在于pop()函数,如果val > 队尾就一直pop(),把右边的小的都pop出去然后把大的放在左边;到时候直接返回队首就能get最大值
from collections import deque
class myqueue:
def __init__(self):
self.queue = deque()
def pop(self, val):
if self.queue and val == self.queue[0]: #要pop不一定真移除,要判断一下是否是val == 队首元素;因为有可能已经不在了
self.queue.popleft()
def push(self, val):
while self.queue and val > self.queue[-1]:
self.queue.pop()
self.queue.append(val)
def front(self):
return self.queue[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
queue = myqueue()
res = []
for i in range(k):
queue.push(nums[i])
res.append(queue.front())
for i in range(k, len(nums)):
queue.pop(nums[i-k])
queue.push(nums[i])
res.append(queue.front())
return res
补充:deque
相比于list实现的队列,deque实现拥有更低的时间和空间复杂度。
list实现出队(pop)和插入(insert)时的空间复杂度大约为O(n),
deque在出队(pop)和入队(append)时的时间复杂度是O(1)。
这是因为:列表实现是基于数组的。pop(0)从列表中删除第一个项,它需要左移len(lst) - 1个项来填补空白。
deque()实现使用双向链表。因此无论deque有多大,deque.popleft()都需要一个常量的操作数。
求前k个高频元素(优先级队列O(nlogn))
这个题是用优先级队列的,优先级队列就是用大顶堆或者小顶堆实现的
建堆O(n),调整O(logn);
主要学一下heapq 之前的博客
1.建堆, 注意建的堆是小顶堆
heap = []
for item in [2, 3, 1, 4]:
heapq.heappush(heap, item)
print heap # 输出 [1, 3, 2, 4]
或者
heap = [2,3,1,4]
heapq.heapify(heap)
print heap # 输出 [1, 3, 2, 4]
2.heapq.heappush()
heapq.heappush(heap名字,元素)
heapq.heappush(heap, -10)
print heap # 输出 [-10, 1, 2, 4, 3]
3.heapq.heappop(heap名字)
print heapq.heappop(heap) # 输出 -10
print heap # 输出 [1, 3, 2, 4]
4.heapq.nlargest(n, heap的名字) # 前n大的数
heapq.nsmallest(n, heap的名字) #前n小的数
l = [2, 3, 1, 4]
print heapq.nlargest(2, l)
1.要统计元素出现频率
2.对频率建立小顶堆
3.找出前K个高频元素
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
num_dict = collections.Counter(nums)
heap = []
for key, freq in num_dict.items():
heapq.heappush(heap, (freq, key))
if len(heap) > k: heapq.heappop(heap)
res = [0] * k
for i in range(k):
res[i] = heapq.heappop(heap)[1]
return res[::-1]