代码随想录算法训练营第13天 | 239. 滑动窗口最大值,347.前 K 个高频元素

239.  滑动窗口最大值

题目链接:https://leetcode.cn/problems/sliding-window-maximum

解法:

不能用大顶堆。大顶堆(优先级队列)可以存放这个窗口里的k个数字,这样就可以知道最大的最大值是多少了, 但是问题是这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。所以不能用大顶堆。

应该维护一个单调队列,从大到小。放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,移除上一步的最大值。每次移动之后,队列告诉我们里面的最大值是什么。

队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。

这个单调队列需要自己实现。

边界条件:无

时间复杂度:O(n)

空间复杂度:O(k)

from collections import deque
class MyDeque:
    def __init__(self):
        self.queue = deque()
    
    # 移动一步到下一个窗口时,需要把上一个窗口开头的元素移除掉
    # 当且仅当这个元素是单调数列中的最大元素,才需要移除,所以popleft
    # 因为这个元素可能成为下一步的最大元素,但不属于下一个窗口
    def pop(self,value):
        if self.queue and value == self.queue[0]:
            self.queue.popleft()
    
    # 由于队列是从大到小的,所以append的元素必须是最小的,
    # 因此如果当前元素大于最后一个元素,那么不满足于单调递减,需要pop
    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:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        queue = MyDeque()
        result = []
        # 先插入前k个元素并且获取第一个窗口的最大值
        for i in range(k):
            queue.push(nums[i])
        result.append(queue.front())
        # 窗口每滑动一步,都从队列中移除上一步的最大值
        for i in range(k, len(nums)):
            if nums[i-k] == queue.front():
                queue.pop(nums[i-k])
            queue.push(nums[i])
            result.append(queue.front())
        return result        

347.  前 K 个高频元素

题目链接:https://leetcode.cn/problems/top-k-frequent-elements

解法:

首先统计元素出现的频率,这一类的问题可以使用map来进行统计。

然后是对频率进行排序,这里可以使用优先级队列,优先级队列内部元素是自动依照元素的权值排列。

大顶堆(堆头是最大元素),小顶堆(堆头是最小元素),从小到大排就是小顶堆,从大到小排就是大顶堆。

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

边界条件:无

时间复杂度:O(nlogk)

空间复杂度:O(n)

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        count = {}
        # 统计数字的频率
        for num in nums:
            count[num] = count.get(num, 0) + 1
        
        # 构建优先队列,小顶堆,因为小顶堆每次弹出的是最小的元素
        prio_queue = []
        for key, freq in count.items():
            # 注意,插入时的元组,频率在前,元素在后
            heapq.heappush(prio_queue, (freq, key))
            # 如果元素超过k个,则弹出最小的元素
            if len(prio_queue) > k:
                heapq.heappop(prio_queue)
        # 小顶堆每次弹出最小的元素,因此最后按倒序来输出
        result = [0] * k
        for i in range(k-1, -1, -1):
            # 弹出的元素,取第二个值
            result[i] = heapq.heappop(prio_queue)[1]
        return result

你可能感兴趣的:(数据结构和算法,算法)