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

239. 滑动窗口最大值 (一刷至少需要理解思路

之前讲的都是栈的应用,这次该是队列的应用了。本题算比较有难度的,需要自己去构造单调队列,建议先看视频来理解。 

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

这道题的整体思路是建立一个自己的特殊队列,要求这个队列保证单调不增。这样每次求最大值的时候直接取队列的front元素就行。要注意一点,可能成为最大值的除了当前的最大值还有之后的值,故而当前窗口涉及的值中,如果后面的值更大,就不需要存储前面的值,反之却还是要存储后面的值的,因为前面的值更早会被pop掉,之后要从后面的值中考虑,在一个窗口中不是最大,要考虑可能在下一个窗口中成为最大,却不用考虑在当前窗口最大值之前的值,因为无论如何都不可能成为接下来的最大值了。具体要实现三个函数:pop()、push()、getvalue()。

首先要说明的是这道题使用的是deque来实现队列,而deque也是默认的队列实现方式,它是双向队列,可以在两头进行插入和删除。

pop函数要做的就是判断一下要pop的数值大小,如果比最大值(也就是队列的前端)小,那就证明已经在最大值入队的时候就已经被pop掉了,如果等于最大值才要真正地执行pop操作。

push函数要做的就是,将要push的值从队列尾部(最小值)依次比较,一直把比(要push的值)小的值全部pop掉,才会正式push进去。这样就保证留下的全是可能成为最大值的值。

这里整体主函数我只用了一次for循环,是使用if语句来实现为头一个窗口的队列初始化的。

class Solution {
private:
    deque que;
    void pop(int value){
        if(que.size()&&value==que.front()) que.pop_front();
    }
    void push(int value){
        while(que.size()&&value>que.back()) que.pop_back();
        que.push_back(value);
    }
    int getvalue(){
        return que.front();
    }
public:
    vector maxSlidingWindow(vector& nums, int k) {
        vector res;
        for(int i=0;ipush(nums[i]);
            if(i-k>=0) this->pop(nums[i-k]);
            if(i>k-2) res.push_back(this->getvalue());
        }
        return res;
    }
};

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

大/小顶堆的应用, 在C++中就是优先级队列。本题是 大数据中取前k值 的经典思路,了解想法之后,不算难。

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

当然能想到哈希表来做,但是还是看文章有什么更好的做法。文章首先用map来储存次数,使用优先级队列来达到排序的效果,保证队列里面只保存当前遍历到的键值对中最大的k个,如果超过,就立马pop掉最小的那一个,因为pop掉的是队列头部元素,也就是堆顶,所以要使用最小堆来保证每次pop掉的一定是最小的。

值得一提的是,这里对运算符()进行重载,使用的最小堆,但是判断规则是左边的大于右边的值为true,文章里面提到了但没有细究,只推断是定义有关。

特别注意这几行代码:

    // 小顶堆
    class mycomparison {
    public:
        bool operator()(const pair& lhs, const pair& rhs) {
            return lhs.second > rhs.second;
        }
    };
    // 定义一个小顶堆,大小为k
    priority_queue, vector>, mycomparison> pri_que;

    // 使用迭代器扫map
    for (unordered_map::iterator it = map.begin(); it != map.end(); it++) 
     
class Solution {
public:
    // class mycomparison{
    //     bool operator() (const pair &lhs,const pair &rhs){
    //         return lhs.second < rhs.second;
    //     }
    // };
    class mycomparison {
    public:
        bool operator()(const pair& lhs, const pair& rhs) {
            return lhs.second > rhs.second;
        }
    };
    
    vector topKFrequent(vector& nums, int k) {
        unordered_map map;
        for(int i=0;i,vector>,mycomparison> prio_que;
        for(unordered_map::iterator it=map.begin();it!=map.end();it++){
            prio_que.push(*it);
            if(prio_que.size()>k) prio_que.pop();
        }
        vector res(k);
        for(int i=k-1;i>=0;i--){
            res[i]=prio_que.top().first;
            prio_que.pop();
        }
        return res;
    }
};

总结 

栈与队列做一个总结吧,加油

代码随想录

栈是容器适配器,底层容器使用不同的容器,导致栈内数据在内存中不一定是连续分布的。缺省情况下,默认底层容器是deque在内存中的数据分布是不连续的。

递归的实现是栈:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,因而可以返回上一层位置。

栈的经典问题:括号匹配、字符串去重、逆波兰表达式求值。

单调队列不是一成不变的,而是不同场景不同写法。
优先级队列就是一个披着队列外衣的堆。
因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的完全二叉树

队列经典问题:滑动窗口最大值(单调队列)、求前k个高频元素。

你可能感兴趣的:(代码随想录训练营,算法)