前言
今天是队列专项:都需要思考,不是很难,重新做
链接
原来csdn的粉丝都是机器人,我说怎么那么多人,好吧,我随便记录一下,既然没有人看
239. 滑动窗口最大值
💖非常典型的队列题目
思路
暴力求解: 遍历一遍的过程中每次从窗口中再找到最大的数值,这样很明显是O(n × k)的算法
总体思路: 此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。
保证队列里的元素单调递减: 队列里面放入一个元素就和前面一个元素比较,如果比它大就pop前面的,如果比它小再放进去; [有动图]
设计单调队列的时候,pop,和push操作要保持如下规则:
- pop(value):如果窗口向后移动要移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出【大于入口说明大于所有的,前面所有的都弹出】,直到push元素的数值小于等于队列入口元素的数值为止
自己写 注意事项【最好重新做一遍】
- python的deque的index也是从0开始从最左边开始标;
- 怎么方便怎么来:pop和popleft都用到了
- 所有的pop操作都需要判断是否为空【我在写myqueue的pop就忘了】
- push的def再看一下写法
from collections import deque
class MyQueue: #单调队列(从大到小
def __init__(self):
self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时
#每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
#同时pop之前判断队列当前是否为空。
def pop(self, value):
if self.queue and value == self.queue[0]:
self.queue.popleft()#list.pop()时间复杂度为O(n),这里需要使用collections.deque()
#如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
#这样就保持了队列里的数值是单调从大到小的了。
def push(self, value):
while self.queue and value > self.queue[-1]:
self.queue.pop()
self.queue.append(value)
#查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
def front(self):
return self.queue[0]
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
que = MyQueue()
result = []
for i in range(k): #先将前k的元素放进队列
que.push(nums[i])
result.append(que.front()) #result 记录前k的元素的最大值
for i in range(k, len(nums)):
que.pop(nums[i - k]) #滑动窗口移除最前面元素
que.push(nums[i]) #滑动窗口前加入最后面的元素
result.append(que.front()) #记录对应的最大值
return result
347.前 K 个高频元素
思路
知识补充:大顶堆和小顶堆注意这个里面使用大小顶堆进行排序
- 本题中使用小顶堆:因为小顶堆最上面的是最小的,会弹出去,把小的都弹出去之后留下大的;
- python使用heapq来做顶堆:heapqpush(ans, ele_v)的时候放入堆,它会自动排序;headpop弹出最顶部的那个;
这道题目主要涉及到如下三块内容:
- 要统计元素出现频率
- 对频率排序
- 找出前K个高频元素
💘统计频率:使用map来做呀,这时候要记住了
排序:通常是使用快排,返回前k个,但是由于这里只需要k,所以只用对k个元素进行维护;
具体做法:遍历的时候使用小顶堆,当小顶堆长度大于k的时候,就pop出去根节点最小的,这样保证留下来的都是k大的;
但是由于小顶堆pop出去的都是从小到大,题目要求从大到小;方法:在装入result数组 的时候,从后往前装–1
方法一 使用顶堆
注意事项
- 在 搭建小顶堆的时候,push进去的是键值对,但是value要写在前面,因为根据value来进行堆的构建:heapq.heappush(pri_que, (freq, key))
相应的,pop之后也是一对以(freq, key)排序的,加入result的时候append ele[1]:heapq.heappop(pri_que)[1] - 要记住了:map_[nums[i]] = map_.get(nums[i], 0) + 1这一行代码来统计频率
- 创建小顶堆也是用的数组;pri_que
- 倒序修改list需要先创建一个那么大的list[0]*K
import heapq
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
map_ = {}
for i in range(len(nums)):
map_[nums[i]] = map_.get(nums[i],0)+1
pri_que = []
for key, value in map_.items():
heapq.heappush(pri_que,(value,key))
if len(pri_que)>k:
heapq.heappop(pri_que)
result = [0]*k
for i in range(k-1,-1,-1):
result[i] = heapq.heappop(pri_que)[1]
return result
方法二 使用字典
普通排序的方法
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 使用字典统计数字出现次数
time_dict = defaultdict(int)
for num in nums:
time_dict[num] += 1
# 更改字典,key为出现次数,value为相应的数字的集合
index_dict = defaultdict(list)
for key in time_dict:
index_dict[time_dict[key]].append(key)
# 排序
key = list(index_dict.keys())
key.sort()
result = []
cnt = 0
# 获取前k项
while key and cnt != k:
result += index_dict[key[-1]]
cnt += len(index_dict[key[-1]])
key.pop()
return result[0: k]
总结-栈与队列的总结
知识点的补充