算法D13 | 栈与队列练习2 | 单调队列 | 239. 滑动窗口最大值 347.前 K 个高频元素

本文介绍了如何利用单调队列和优先级队列(最小堆)解决编程问题,包括滑动窗口中的最大值查找和前K个高频元素的统计。作者提供了Python和C++的代码示例,强调了栈与队列在数据结构中的应用和区别。
今日继续练习栈与队列并总结。
239. 滑动窗口最大值 (一刷 至少需要理解思路

之前讲的都是栈的应用,这次该是队列的应用了。

本题算比较有难度的,需要自己去构造单调队列,建议先看视频来理解。 

题目链接/文章讲解/视频讲解:代码随想录

这个题主要思路是用单调队列实现,如果一直在滑动窗口内部排序的话,O(nlogn)就是内部最优了,这样的暴力解法时间上没啥优化。但是单调队列可以让滑动窗口内部只保留单调部分元素(本题是求最大值,那么就保持递减,每次pop出第一个元素即为窗口内最大值),这样的操作滑动窗口内部时间复杂度是O(n),时间复杂度变成线性的了。

Python版本:

Python没有单调队列这个数据类型,于是在deque的基础上重新定义这样一个类。

from collections import deque

class SingleDirQueue:
    def __init__(self):
        self.queue = deque()

    def pop(self, value):
        if self.queue and value==self.queue[0]:
            self.queue.popleft()
    
    def push(self, value):
        while self.queue and value>self.queue[-1]:
            self.queue.pop()
        self.queue.append(value)
    
    def front(self):
        return self.queue[0]

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        queue = SingleDirQueue()
        result = []
        for i in range(k):
            queue.push(nums[i])
        result.append(queue.front())
        for i in range(k, len(nums)):
            queue.pop(nums[i-k])
            queue.push(nums[i])
            result.append(queue.front())
        return result
        

C++版本:

C++内可以用multiset这个数据类型来实现,也可以自定义一个单调队列的类来实现。以下是自定义版本,multiset版本思路一样,只是que底层数据结构变成直接调用了。

class Solution {
private:
    class SingleDirQueue {
    public:
        deque<int> que;
        void pop(int value) {
            if (!que.empty() && value==que.front()) {
                que.pop_front();
            }
        }
        void push(int value) {
            while (!que.empty() && value >que.back()) {
                que.pop_back();
            }
            que.push_back(value);
        }
        int front() {
            return que.front();
        }
    };

public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        SingleDirQueue que;
        vector<int> result;
        for (int i=0; i<k; i++) {
            que.push(nums[i]);
        }
        result.push_back(que.front());
        for (int i=k; i<nums.size(); i++) {
            que.pop(nums[i-k]);
            que.push(nums[i]);
            result.push_back(que.front());
        }
        return result;
    }
};

347.前 K 个高频元素  (一刷至少需要理解思路)

大/小顶堆的应用, 在C++中就是优先级队列 

本题是 大数据中取前k值 的经典思路,了解想法之后,不算难。

题目链接/文章讲解/视频讲解:代码随想录

这个题用到了最大最小堆(堆容量为k的,只保留最大的k个元素,最大/最小底层都是一样的)。这里需要保留最大的k个元素,应使用最小堆,每次pop弹出堆内最小的元素,只保留大的元素。两种语言队列里的元素都是item<key, value>, 可以在不同的情况下根据key/value来排序或者选择,都算比较灵活的设计。

Python版本:

python 用heapq实现。

import heapq

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        freq_cnt = {}
        for i in range(len(nums)):
            freq_cnt[nums[i]] = freq_cnt.get(nums[i], 0) + 1
        
        pri_que = []
        for num, freq in freq_cnt.items():
            heapq.heappush(pri_que, (freq, num))
            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

C++版本:

C++用priority_queue:优先级队列实现。最小堆底层是二叉树,对于每个parent,子代left>right即可;反之是最大堆。也可以只掌握一个,element取相反数即可。

class Solution {
public:
    class minHeap {
    public:
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
            return lhs.second > rhs.second;
        }
    };

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> freq_cnt;
        for (int i=0; i<nums.size(); i++) {
            freq_cnt[nums[i]]++;
        }        

        priority_queue<pair<int, int>, vector<pair<int, int>>, minHeap> pri_que;

        for (unordered_map<int, int>::iterator it=freq_cnt.begin(); it!=freq_cnt.end(); it++) {
            pri_que.push(*it);
            if (pri_que.size()>k) {
                pri_que.pop();
            }
        }

        vector<int> result(k);
        for (int i=k-1; i>=0; i--) {
            result[i] = pri_que.top().first;
            pri_que.pop();
        }
        return result;
    }
};
总结 

栈与队列做一个总结吧,加油。代码随想录

栈与队列 和数组本质的区别在于是否可以循秩访问,

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值