Day13 栈与队列part03
239. 滑动窗口最大值
队列的应用:需要自己去构造单调队列
**思路:**希望构造一种单调队列,这个队列中元素是排序的,且最大值在顶部,同时我们可以随着滑动窗口修改其中的元素
其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。
push和pop的操作:对于这个队列来说要单调递减,所以顶端开头的元素要一直是最大的;push:如果队列中补充的元素都小于前项队尾元素,相当于append;pop:当滑动窗口滑出时或者新push进来的队尾元素大于队头元素
- 时间复杂度: O(n)
- 空间复杂度: O(k)
再来看一下时间复杂度,使用单调队列的时间复杂度是 O(n)。有的同学可能想了,在队列中 push元素的过程中,还有pop操作呢,感觉不是纯粹的O(n)。其实,大家可以自己观察一下单调队列的实现,nums 中的每个元素最多也就被 push_back 和 pop_back 各一次,没有任何多余操作,所以整体的复杂度还是 O(n)。
from collections import deque
class MyQue:
def __init__(self):
self.que = deque()
def pop_left(self, value):
'''这里pop的作用是:pop顶端的最大值,只适用于滑动窗口已经不包含这个数的情况'''
if self.que and self.que[0] == value:
self.que.popleft()
def push_que(self, value):
'''push的作用:要把新的数push进来,同时要保证前面的数都比这个value要大,如果小的话从队尾pop出去'''
while self.que and self.que[-1] < value:
self.que.pop()
self.que.append(value)
def front(self):
'''返回队头元素,也就是最大的这个元素'''
return self.que[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
que = MyQue()
res = []
# 先初始化,在这种情况下是没有需要pop的元素,因为不存在越界问题
for i in range(k):
que.push_que(nums[i])
res.append(que.front())
# print(res)
for i in range(k, len(nums)):
que.pop_left(nums[i-k]) # 先清除越界元素
que.push_que(nums[i]) #重点push操作
res.append(que.front()) # 即使没有输入也要记得加括号
# print(i, res)
return res
347.前 K 个高频元素
这道题的基本思路:
- 要统计元素出现频率:用C++中的map,类似py中的dict:{key(值), value(出现频率)}
- 对频率排序:采用从队头取元素,从队尾添加元素的优先级队列,其实也就是堆
- 找出前K个高频元素
大顶堆和小顶堆的区别:大顶堆是顶上元素大,子节点元素小,先pop大的元素;小顶堆相反,我们本题只能采用小顶堆才能不停把小的pop出去,保存大的
补充:heapq-堆排序算法
Python中heapq模块浅析_heapq.heappush-CSDN博客
堆是一种数据结构,它是一颗完全二叉树。
- heappush(heap,item)建立大小顶堆,默认建立小顶堆:
'''小顶堆构造法'''
import heapq
a = [] #创建一个空堆
heapq.heappush(a,18)
heapq.heappush(a,1)
heapq.heappush(a,20)
heapq.heappush(a,10)
heapq.heappush(a,5)
heapq.heappush(a,200)
print(a)
输出:[1, 5, 20, 18, 10, 200]
'''大顶堆构造法:加负号'''
a = []
for i in [1, 5, 20, 18, 10, 200]:
heapq.heappush(a,-i)
print(list(map(lambda x:-x,a)))
输出:[200, 18, 20, 1, 10, 5]
- heapify(heap)建立大、小根堆,将列表直接转换为堆
- heappop(heap):从堆中弹出并返回最小的值
若是从大到小排列,有两种方法:1)先建立小根堆,然后每次heappop(),此时得到从小大的排列,再reverse2)利用相反数建立大根堆,然后heappop(-元素)。即push(-元素),pop(-元素)
本题思路:
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
map_ = {}
for i in nums:
if i not in map_:
map_[i] = 1 #注意初始化
else:
map_[i] += 1
a = []
for key, freq in map_.items(): #注意这个.items()
heapq.heappush(a, (freq,key)) #扫描所有频率的数值!所以以频率排序,频率在前!
if len(a) > k:
heapq.heappop(a)
result = [0]*k
for i in range(k-1, -1, -1): #反着输出才是从大到小
result[i] = heapq.heappop(a)[1]
return result