Day13代码随想录栈与队列基础part03-239. 滑动窗口最大值、347.前 K 个高频元素

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 个高频元素

这道题的基本思路:

  1. 要统计元素出现频率:用C++中的map,类似py中的dict:{key(值), value(出现频率)}
  2. 对频率排序:采用从队头取元素,从队尾添加元素的优先级队列,其实也就是堆
  3. 找出前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
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值