LC 239. 滑动窗口最大值
题目链接: LC 239. 滑动窗口最大值
思路:用单调队列,不是优先级队列,来解决该问题。单调队列在STL中没有这样的数据结构,需要自己创建。**单调队列应该满足的功能:队列元素应是从大到小排列。**当窗口向后移动时,有元素进窗口,也有元素出窗口。
对于进窗口的元素,若遇到比队列头大的元素就将队列中的元素全部弹出,再让大元素进入队列;若遇到比队列头元素小,比队列尾元素大的,就弹出队尾元素直到比遍历的元素大,再将遍历元素放入队列中;若遇到比队列尾元素还小的元素就直接放到队列尾部。总结,就是从队列尾部开始弹直到队列尾部元素大于准备进栈元素。
对于出窗口的元素,若其值不等于队列头元素(最大值)则不用管,若为队列头元素,则将队列头元素弹出窗口。
在处理完进出窗口元素后,队列头元素就是这个窗口的最大值。
判断虽然有点多,但是比暴力解法快,该思路仅用线性复杂度就可解决。
使用deque比较合适,deque是双向队列。虽然stack和queue不支持迭代器,但是deque支持。deque的STL函数:
deque<int> de;
de.front();
de.back();
de.push_front();
de.push_back();
de.pop_front();
de.pop_back();
de.size();
//还有好多支持迭代器的函数如
de.begin();
de.end();
de.insert();
代码:
class Solution {
//先构建一个单调队列
private:
class MyQueue{
public:
//定义一个双向队列
deque<int> de;
//双向队列所有用函数
//进队列
void push(int x){
//x是入窗口元素
//当队列非空且最后元素小于入栈元素时,弹出队尾元素
while(!de.empty() && de.back()<x){
de.pop_back();
}
de.push_back(x);
}
//出队列,若出窗口的元素为队列最大元素,则弹出队头元素,否则不用管。
//为什么可以这样?首先,单调队列中的元素的顺序是按着遍历的顺序来的,所以先进来的必定先出去。
void pop(int x){
//x是出窗口元素,要不要判断队列为空都行
if(!de.empty() && x == de.front())
de.pop_front();
}
//得到队列头节点
int front(){
return de.front();
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;//创建自己的单调队列
vector<int> result;//保存结果
for(int i=0; i<k; ++i){//把前k个先放进栈中
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;
}
};
LC 347.前 K 个高频元素
题目链接:LC 347.前 K 个高频元素
思路:首先遍历vector,构建map,key为vector的值,value为值的个数。然后使用优先级队列(小顶堆或者大顶堆),构造大小为K的优先级队列,因为要保存频率最大的K个元素,所以使用小顶堆合适。每次弹出最小的元素(堆顶元素),最后保存的K个就是最大的。或者不对堆进行pop,只push,用大顶堆保存,最后遍历前k个就可以。
对于堆这种数据结构,每次pop都是从堆顶开始pop,每次push都是从堆底push,然后对堆进行调整。
对于堆,C++有现成的数据结构可以调用为priority_queue,其包含的成员函数有:
//定义priority_queue
priority_queue<int, vector<int>, myCompare> prioQue;//第一个参数为堆包含的元素类型;第二个参数为基础容器的对象类型(用什么容器保存元素);第三个参数为用于对优先级队列排序的比较对象(函数指针或者函数对象)
//myCompare有两种方式:第一种是直接使用STL的仿函数less或者greater;第二种为自己实现
//第一种:直接使用STL的仿函数,适用于简单的数据结构
#include<functional>//这两个函数在头文件functional中
priority_queue<int, vector<int>, less<int>> prioQue;
priority_queue<int, vector<int>, greater<int>> prioQue;
//第二种:自己构建,适用于复杂的数据结构
class myCompare{
public:
bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs){
return lhs.second > rhs.second;//大于号是小顶堆,小于号是大顶堆
}
};
priority_queue<pair<int, int>, vector<pair<int, int>>, myCompare> prioQue;
prioQue.pop();//弹出第一个元素,堆顶元素,弹出后进行堆的结构调整。弹出的具体流程为:先把堆顶元素与尾元素交换,然后删除尾元素,最后调整树的结构形成新的堆
prioQue.push();//按排序顺序插入新元素,插入元素不用调整结构
prioQue.size();//堆中元素总数
prioQue.top();//返回堆中第一个元素
代码:
class Solution {
public:
class myCompare{
public:
bool operator()(const pair<int, int>& a, const pair<int, int>& b){//重载operator运算符可传入无限参数
//由于传入的是引用,所以需要const
return a.second > b.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
//使用map,将每个整数出现的频率保存下来
unordered_map<int, int> map;
// for(int i=0; i<nums.size(); ++i){
// map[nums[i]]++;
// }
for(int i : nums){
++map[i];
}
//使用小顶堆保存前k个数据对
priority_queue<pair<int, int>, vector<pair<int, int>>, myCompare> que;
// for(unordered_map<int, int>::iterator j=map.begin(); j!=map.end(); ++j){
// que.push(*j);
// if(que.size()>k)que.pop();//若队列大小大于k,就弹出
// }
for(pair<int, int> j : map){
que.push(j);
if(que.size() > k)que.pop();
}
vector<int> result(k);//大小为k的vector
for(int m=k-1; m>=0; --m){
result[m] = que.top().first;
que.pop();
}
return result;
}
};