准备知识
-
优先级队列
优先级队列本质上是一个堆,因为其接口函数使用的操作和队列非常类似,所以被称为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
来实现单调队列,我觉得本题比较难的就是如何排出窗口内的最大值,但是在滑动过后不在窗口范围内的情况,所以需要考虑遍历时队列push
和pop
的顺序,以及pop
时的判断。push
的逻辑是不需要维护窗口内所有的元素,只需要维护可能为最大值的元素,如果当前元素大于队列中的元素,就先把队列中pop
再push
,如果当前元素小于队尾的元素,直接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 <functional>
中的less
和greater
也可以实现大小顶堆。
可以看到priority_queue
的数据类型为pair<int,int>
,这是比较特殊的地方。
在对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
操作即可。