代码随想录第十三天 LeetCode 239、347(队列)

准备知识

  • 优先级队列
    优先级队列本质上是一个,因为其接口函数使用的操作和队列非常类似,所以被称为priority_queue。创建一个优先级队列对象需要确认:1.数据类型;2.存放数据的容器;3.比较函数。在创建时参数缺省的情况下,默认使用vector作为容器以及大顶堆的存放方式。(堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。)下面看一下优先级队列在c++中的语法:

    priority_queue<int,vector<int>,function> que; //function为比较函数
    priority_queue<int> que; //大顶堆以vector为表现形式的complete binary tree(完全二叉树)
    que.push(i);
    que.pop();
    que.empty();
    que.size()

    优先级队列的pop操作是从堆顶开始的,也就是如果是大顶堆会pop最大值,小顶堆就会pop最小值。

239. 滑动窗口最大值

本题的标注难度为困难,思路不难,就是实现比较难想明白。本题卡哥给出的方法是自己实现一个单调队列,即滑动窗口时自己维护一个从大到小排序的队列,这样就可以在符合条件的时候直接使用队列top的元素作为答案输出,所以关键就是造一个符合题目要求的单调队列。
这里使用deque来实现单调队列,我觉得本题比较难的就是如何排出窗口内的最大值,但是在滑动过后不在窗口范围内的情况,所以需要考虑遍历时队列pushpop的顺序,以及pop时的判断。push的逻辑是不需要维护窗口内所有的元素,只需要维护可能为最大值的元素,如果当前元素大于队列中的元素,就先把队列中poppush,如果当前元素小于队尾的元素,直接push
直接使用代码解释:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ans;
        deque<int> que;
        //需要提前把第一个窗口的元素输入,因为这里不涉及滑动的逻辑。
        //同时可以解决nums.size()==k的特殊情况
        //这里也要遵循单调队列的push逻辑
        for (int i = 0 ; i < k ; i++) {
            while (!que.empty() && nums[i] > que.back()) {
                que.pop_back();
            }
            que.push_back(nums[i]);
        }
        //此时已经有第一个窗口的最大值了
        ans.push_back(que.front());
        for(int i = k; i < nums.size(); i++){
        	//push逻辑
            while (!que.empty() && nums[i] > que.back()) {
                que.pop_back();
            }
            que.push_back(nums[i]);
            //判断队首元素是否以及不在窗口中
            if(que.front() == nums[i - k]){
                que.pop_front();
            }
            ans.push_back(que.front());
        }
        return ans;
    }
};

我认为本题比较关键的地方就是处理单调队列逻辑时的顺序,先push再判断是否需要pop最后输入答案。

347.前 K 个高频元素

本题需要前面介绍的优先级队列,对频率进行排序。思路还是很清晰的,首先使用一个map遍历数组,然后记录下每个元素出现的次数作为value。然后使用优先级队列对map进行遍历输出答案。优先级队列的内部实现时通过堆,本题应该使用小顶堆,因为其先进先出并且pop时弹出堆顶元素的特性决定了不应该使用大顶堆。
因为是对map遍历,所以优先级队列应该这样定义:

priority_queue<pair<int,int>, vector<pair<int, int>> , mycomparison> que;

mycomparison定义如下:

// 小顶堆
    class mycomparison {
    public:
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
            return lhs.second > rhs.second;
        }
    };

其实这里我也不太懂,好像使用头文件#include 中的lessgreater也可以实现大小顶堆。

可以看到priority_queue的数据类型为pair,这是比较特殊的地方。
在对map进行遍历的时候,优先级队列中只维护k个元素,当队列的size大于k时,就pop
完整代码如下:

class Solution {
public:
    // 小顶堆
    class mycomparison {
    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) {
        map<int,int> map;
        for(int i = 0; i < nums.size(); i++){
            map[nums[i]]++;
        }
        priority_queue<pair<int,int>, vector<pair<int, int>> , mycomparison> que;
        for(auto it = map.begin(); it != map.end(); it++){
            que.push(*it);
            if(que.size() > k) que.pop();
        }
        vector<int> ans(k);
        for(int i = k - 1; i >=0; i--){
            ans[i] = que.top().first;
            que.pop();
        }
        return ans;
    }
};

因为小顶堆的特性,所以最后输出答案从vector尾部开始,输入一个答案堆队列进行一次pop操作即可。

你可能感兴趣的:(leetcode,算法,c++)