【leetcode刷刷】239. 滑动窗口最大值 、347.前 K 个高频元素 、栈与队列总结

239. 滑动窗口最大值

  1. 所以deque是双向都可以pop的吗。
  2. 单调队列,每次做都觉得比较难想,不知道怎么根据题目设计单调队列或单调栈。
  3. 本题:需要的是最大值,因此每次输出的时候是最大值,直到这个最大值被pop出去,后面一个最大值顶上。因此是一个递减的单调队列。为了维持递减这个属性,每次append的时候,需要比较队尾元素和append的数,小于value的就popright,然后append进去。
  4. 一个问题:为什么front的时候单调队列里一直有数。因为每次front前都有一个append,因此一定会有数。
  5. 时间复杂度O(n),空间复杂度O(k)
from collections import deque

class queue():
    def __init__(self):
        self.q = deque() # 这是个双面队列???
    
    def pop(self, value):
        if self.q and self.q[0] == value: # 如果q[0]等于value,说明pop出最大值了
            self.q.popleft()  # pop左边的,大的

    def append(self, value):
        while self.q and value > self.q[-1]:  # 直到q[-1]大于等于value,才能append进去
            self.q.pop()  # pop右边的,小的?
            # print(self.q)
        self.q.append(value)

    def front(self):
        return self.q[0]


class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        # 单调队列,每次pop,出队的是先入的,同时输出一个最大值。
        # 也就是在pop的时候维护一个最大值
        q = queue()
        result = []
        for i in range(k):
            q.append(nums[i])
        result.append(q.front())
        for i in range(k, len(nums)):
            # print(q.q)
            q.pop(nums[i-k])
            # print(q.q)
            q.append(nums[i])
            result.append(q.front())
        return result



        # 窗口是一个队列,先入先出
        stack = [] 
        for i in range(k):
            stack.append(nums[i])
        output = []
        # O(n*k):超出时间限制
        max_ = max(stack)
        output.append(max_)
        for i in range(k, len(nums)):
            stack.pop(0)
            stack.append(nums[i])
            if nums[i] > max_:
                max_ = nums[i]
            else:
                max_ = max(stack)
            output.append(max_)
        
        return output  

347. 前k个高频元素

  1. 所以heapq是可以直接用的?heapq.heappush是堆的push,heapq.heappop是堆的pop。那怎么确定是大顶堆还是小顶堆的?
  2. 时间复杂度O(nlogk),空间复杂度O(n)
  3. heap可以是一个元组(freq,key)?
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        counter = Counter(nums)
        
        # 前k个, 单调栈,k个都在栈里
        # 优先级队列——堆

        #对频率排序
        #定义一个小顶堆,大小为k
        pri_que = [] #小顶堆
        
        #用固定大小为k的小顶堆,扫描所有频率的数值
        for key, freq in counter.items():
            heapq.heappush(pri_que, (freq, key))
            if len(pri_que) > k: #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
                heapq.heappop(pri_que)
        
        #找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
        result = [0] * k
        for i in range(k-1, -1, -1):
            result[i] = heapq.heappop(pri_que)[1]
        return result

总结

  1. 单调栈/单调队列迷迷糊糊的
  2. 堆/优先级队列也迷迷糊糊的
  3. 以前应该是学过的,大顶堆小顶堆二叉树什么的,难过了,明天恶补一下

215. 数组中的第k个最大元素

  1. 动手写了一下大顶堆,大概就是用一个数组维护一个大顶堆的完全二叉树(不过为什么不是直接用树结构?二叉树的底层是数组?),需要写add和pop两个函数。
  2. add的时候,加的数加在完全二叉树的末尾,然后通过上移,完成堆的维护。
  3. pop的时候,将堆顶和堆尾进行调换顺序,然后将堆尾的节点pop掉,堆顶的节点下移,完成堆的维护。
  4. python里可以直接用heapq库来调用堆。heapq实现的是小顶堆。因此本地需要的是第k个最大元素的话,就是维护一个k大小的优先级序列,如果输入的数大于这个小顶堆的顶,那么pop出这个最小的元素,push入新的数。
  • heapq.heappush(heap, item):将元素item添加到堆heap中。
  • heapq.heappop(heap):从堆heap中弹出并返回最小(或最大)的元素。
  • heapq.heapify(heap):将列表heap原地转换为一个合法的堆。
  • heapq.heapreplace(heap, item):将堆heap中的最小(或最大)元素弹出,并将元素- item添加到堆中。
  • heapq.heappushpop(heap, item):将元素item添加到堆heap中,并返回堆中的最小(或最大)元素。
  • heapq.nlargest(k, iterable):返回可迭代对象iterable中最大的k个元素。
  • heapq.nsmallest(k, iterable):返回可迭代对象iterable中最小的k个元素。
class MaxHeap():
    def __init__(self):
        self._data = []
        self._count = 0

    def size(self):
        return self._count

    def isEmpty(self):
        return self._count == 0

    def add(self, x):
        self._data.append(x)
        self._shift_up(self._count)   # 最后一个数字换上去n-1
        self._count += 1
        
    def pop(self):
        if self._count:
            ret = self._data[0]   # pop出最大值
            self._count -= 1
            self._data[0] = self._data[-1]  # 把最后一个换到第一个
            self._data.pop()  # 最后一个pop
            self._shift_down(0)   # 第一个数字换下去
            return ret

    # 把index为i的节点往上换。怎么证明自下而上换,一定不用调整别的了?
    def _shift_up(self, index):
        parent = (index - 1)//2    # 根据子节点计算父节点
        # 当index比parent大的时候就上调。index=0就已经是顶了,不用继续上调
        while index > 0 and self._data[index] > self._data[parent]:
            self._data[index], self._data[parent] = self._data[parent], self._data[index]
            index = parent
            parent = (index - 1) // 2

    def _shift_down(self, index):
        max_child = (index << 1) + 1
        while max_child < self._count:
            if max_child + 1 < self._count and self._data[max_child] < self._data[max_child+1]:
                max_child += 1
            if self._data[index] < self._data[max_child]:
                self._data[index], self._data[max_child] = self._data[max_child], self._data[index]
                index = max_child
                max_child = (index << 1) + 1
            else:
                break

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        # 使用api
        heap = []
        for i in range(k):
            heapq.heappush(heap, nums[i])  # 小顶堆
        for i in range(k, len(nums)):
            if nums[i] > heap[0]:   # nums[k]比堆里最小的大,就替换掉。优先级队列?
                heapq.heappop(heap)
                heapq.heappush(heap, nums[i])
        return heap[0]

        # 使用手写堆
        n = len(nums)
        heap = MaxHeap()
        for i in nums:
            heap.add(i)
        for i in range(k):
            result = heap.pop()
        return result
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值