LeetCode 239. 滑动窗口最大值
题目链接:239. 滑动窗口最大值 - 力扣(Leetcode)
单调队列经典题目,暴力解法是遍历每个滑动窗口的起始点,然后max求最大值,但以题目的数据量进行很容易超时(max,min本质上也是O(n)的时间复杂度)。窗口移动的效果可以用先入先出的队列来模拟,利用队列的特性构造单调递减的队列,构造的关键是“队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。”
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
q =collections.deque()
res = []
for i in range(len(nums)):
if not q:
q.append(nums[i])
continue
# i >= k 收集结果
if i >= k:
res.append(q[0]) # 此时的队首是前一个滑动窗口的最大值
# 当前要把nums[i-k]移出队列,如果队首的最大值正好是nums[i-k],需要popleft
if q[0] == nums[i-k]:
q.popleft()
# 当前要加入窗口的元素比队尾大时,要将比它小的所有值出队(从右边出)
while len(q) > 0 and nums[i] > q[-1]:
q.pop()
q.append(nums[i])
res.append(q[0]) # for循环没有收集最后一个窗口的结果
return res
所有元素最多入队和出队各一次,时间复杂度是O(n),队列中最多同时存储k个值,空间复杂度是O(k)。
LeetCode 347.前 K 个高频元素
题目链接:347. 前 K 个高频元素 - 力扣(Leetcode)
首先需要统计每个元素出现的频率,再根据频率排序,这是一般的做法,时间复杂度是O(n+M*logM),其中M为不同元素的个数,最坏情况下M=n;空间复杂度为O(M)。
from queue import PriorityQueue
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count = collections.Counter(nums)
# 用大小为k的优先队列
q = PriorityQueue() # 默认是最小堆
for key in count:
q.put([-count[key], key]) # 按频率取负数排序
res = []
# 取前k个
while k > 0:
_, key = q.get()
res.append(key)
k -= 1
return res