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

栈与队列part03

主要是实现单调队列和运用优先级队列(STL的priority_queue)

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

一刷真的完全没有思路,看了代码随想录的文章讲解和视频讲解才能基本理解思路但是还是不是很清晰,不能做到将思路复现。二刷要重点再理解思路手推一遍过程重新打一遍代码。

个人整理的大致思路应该是这样:

首先是实现一个新的数列(即单调数列),该数列只需要维护可能成为最大值的元素,而不需要维护其他元素,使用最大堆等其他优先级序列无法实现。主要的push流程如下,每一个元素在push时覆盖前面所有比他小的元素。

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

  1. pop(value):如果窗口移除的元素与单调队列的出口元素(即最大值)相等,则需要将出口元素弹出,否则不进行任何操作。(注:其他情况其实是在push的时候由于push值大于队列中入口值,已将窗口要移除的元素弹出)
  2. push(value):如果要push的值value大于单调队列入口的元素,则将入口元素弹出,直到value值小于入口元素或单调队列为空。

保持以上规则,只需返回单调队列出口front元素即为窗口的最大值。

 具体的代码实现如下:

class Solution {
public:
    class MyQueue{
        public:
            deque que;
            void pop(int val){
                if(!que.empty()&&val==que.front()){
                    que.pop_front();
                }
            }
            void push(int val){
                while(!que.empty()&&val>que.back()){
                    que.pop_back();
                }
                que.push_back(val);
            }
            int front(){
                return que.front();
            }
    };
    vector maxSlidingWindow(vector& nums, int k) {
        vector max;
        MyQueue que;
        //首先是处理第一个窗口中的值,返回第一个最大值
        for(int i=0;i

347. 前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

本题首先使用map统计数组中元素的频率,时间复杂度为n

接着使用大小为k的优先级队列(priority queue)的小顶堆对map中的元素进行排序。

难点为小顶堆的定义和原理理解。

什么是优先级队列呢?

其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

而且优先级队列内部元素是自动依照元素的权值排列。那么它是如何有序排列的呢?

缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)。

什么是堆呢?

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

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

来自:代码随想录

优先级队列的定义


C++中,使用优先级队列需要包含头文件,优先级队列的定义如下:

priority_queue

typename是数据的类型;
container是容器类型,可以是vector,queue等用数组实现的容器,不能是list,默认可以用vector;
functional是比较的方式,默认是大顶堆(就是元素值越大,优先级越高);如果使用C++基本数据类型,可以直接使用自带的less和greater这两个仿函数(默认使用的是less,就是构造大顶堆,元素小于当前节点时下沉)。使用自定义的数据类型的时候,可以重写比较函数,也可以进行运算符重载(less重载小于“<”运算符,构造大顶堆;greater重载大于“>”运算符,构造小顶堆)。
原文链接:priority_queue

 代码实现:

class Solution {
public:
    //重写仿函数支持自定义类型
    class mycomparison{
    public:
        bool operator()(const pair &lhs,const pair &rhs){
            return lhs.second>rhs.second;          //小顶堆重载>
        }
    }; 
    vector topKFrequent(vector& nums, int k) {
        //使用map记录
        unordered_map map;
        //统计元素出现频率
        for(int i=0;i, vector>, mycomparison> pri_que;
        //使用小顶堆对map进行扫描
        for(unordered_map::iterator it=map.begin();it!=map.end();it++){
            pri_que.push(*it);
            if(pri_que.size()>k){
                pri_que.pop();
            }
        }
        //将小顶堆中元素倒序输出
        vector result(k);     //vector定义时记得声明大小
        for(int i=k-1;i>=0;i--){
            result[i]=pri_que.top().first;
            pri_que.pop();
        }
        return result;
        
    }

    
};
  • 时间复杂度: O(nlogk)
  • 空间复杂度: O(n)

你可能感兴趣的:(算法)