代码随想录算法训练营第十三天【栈与队列】|239,347

文章介绍了如何使用单调队列解决滑动窗口问题,以求解数组中每k个连续元素中的最大值,同时提到了如何利用优先级队列(小顶堆)在O(nlogk)时间内找到数组中前k个最频繁的元素。
摘要由CSDN通过智能技术生成

239. Sliding Window Maximum(还需再写)

1. 暴力解法O(n*k)

2. 不能用max-heap:因为这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。

3. 需要一个queue,实现三个功能:que.pop(滑动窗口中移除元素的数值),que.push(滑动窗口添加元素的数值),然后que.front()就返回我们要的最大值

4. 单调队列其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。那么这个维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。

5. 设计单调队列的时候,pop,和push操作要保持如下规则:

  1. pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. push(value):如果push的元素value大于入口(右侧)元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止【因为从左到右单调递减,而且push进来的数又大又long-live】

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。

6. time complexity:O(n)

nums 中的每个元素最多也就被 push_back 和 pop_back 各一次,没有任何多余操作,所以整体的复杂度还是 O(n) 

7. 用deque:常用的queue在没有指定容器的情况下,deque就是默认底层容器,直接用list会超时(list.pop()时间复杂度为O(n),这里需要使用collections.deque())

from collections import deque
class  MyQueue:
    def __init__(self):
        self.queue = deque()

    def pop(self,value):
        if self.queue and value == self.queue[0]: #先确定queue不为空 #和出口处(左边)元素比较 queue[0]
            self.queue.popleft()

    def push(self,value):
        while self.queue and value>self.queue[-1]:
            self.queue.pop() #popright
        self.queue.append(value)

    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):
            que.push(nums[i])
        result.append(que.front())
        for i in range(k,len(nums)):
            que.pop(nums[i-k])
            que.push(nums[i])
            result.append(que.front())
        return result



        

347. Top K Frequent Elements(还需再写)

quicksort O(nlogn),所以算法的时间复杂度必须优于此

大顶堆,小顶堆->很大数据集里求前k个高频/低频元素

时间复杂度nlogk ->k个元素的树

此题

  1. 要统计元素出现频率: map
  2. 对频率排序: priority queue
  • priority queue就是一个披着队列外衣的堆, 

    因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。而且优先级队列内部元素是自动依照元素的权值排列

  • 大家经常说的大顶堆(堆头是最大元素),小顶堆(堆头是最小元素),如果懒得自己实现的话,就直接用priority_queue(优先级队列)就可以了,底层实现都是一样的

    3. 找出前K个高频元素

  • 我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

    具体操作:

    import heapq

    heappush

    heappop ->pop最小值,min-heap,留下的都是大的

    import heapq
    class Solution(object):
        def topKFrequent(self, nums, k):
            """
            :type nums: List[int]
            :type k: int
            :rtype: List[int]
            """
            from collections import defaultdict
            dic = defaultdict(int)
            for i in range(len(nums)):
                dic[nums[i]]+=1
            
            pri_que = [] #min-heap
            
            for key,freq in dic.items():
                heapq.heappush(pri_que,(freq,key))
                if len(pri_que)>k: #维持min-heap只有k个元素,heappop最小值
                    heapq.heappop(pri_que)
            
            #如果要从大到小排,要倒叙输出:
            result = [0]*k
            for i in range(k-1,-1,-1):
                result[i] = heapq.heappop(pri_que)[1]
            return result
    
            
            
    
    
            

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值