这题有两种解法,两种解法都是利用双端队列来解。一种是卡哥所说的解法,是以双端队列为基础,构建一个单调队列,这种方法需要改变队列的弹出和插入规则。另一种也是以双端队列为基础,构建一个单调队列,只不过第二种方法存入的是数组的下标。下面将详细讲解两种解法。
解法1:
我们可以构建一个单调队列,去维护每次滑动窗口中的最大值,也就是将每次滑动窗口中的最大值放在队列的头部,这样的话我们得到每次滑动窗口的最大值就只需要在每次滑动窗口的时候,将队列中的头部插入结果数组即可。
思路就是:首先对队列的入队操作,我们需要进行修改。为了保持每次窗口中的最大值在队列头部,我们需要先进行比较在决定是否入队。如果需要入队的元素大于队列中的尾部元素,我们就需要将尾部元素弹出,直到要入队的元素小于或等于尾部元素,此时将该元素插入插入队尾。
然后我们还应该规定一下从头部弹出的元素,也就是为了避免滑动窗口已经往前滑动了,而队头的最大值不在窗口中的情况。也就是将每次要入队的元素在入队前,判断第i-k个元素是否与队头元素相同,如果相同说明这个元素不在窗口,将其弹出。如果不同说明,第i-k个元素已经从队尾被弹出。
具体过程图解如下:
代码如下:
class myqueue
{
public:
deque<int> que;
void pop(int value)
{
if(!que.empty()&&value==que.front())
{
que.pop_front();
}
}
void push(int value)
{
while(!que.empty()&&value>que.back())
{
que.pop_back();
}
que.push_back(value);
}
int front()
{
return que.front();
}
};
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
myqueue que;
for(int i =0;i<k;i++)
{
que.push(nums[i]);
}
vector<int> result;
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;
}
};
解法二:队列中存入数组下标
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
deque<int> que;
for(int i =0;i<nums.size();i++)
{
while(!que.empty()&&i-k+1>que.front())
{
que.pop_front();
}
while(!que.empty()&&nums[i]>nums[que.back()])
{
que.pop_back();
}
que.push_back(i);
if(i+1>=k)
{
result.push_back(nums[que.front()]);
}
}
return result;
}
};
这题的思路我倒是懂了。但是代码方面还是有一些问题,自敲可能还有些困难。
思路就是:先构建一个map结构的哈希表,将每个元素以及其对应出现的频率存入哈希表中。key为元素值,value为该元素出现的频率。然后对value进行排序,找到最大的前k个高频元素。
排序方法有很多,但是大部分都是n*logn复杂度的。而使用小根堆的方法可以将复杂度降低到n*logk。大概思路就是,我们创建一个大小为k的小根堆优先队列,然后往优先队列中加入元素的频率,当加入的元素大于k时,优先队列弹出值,此时弹出的值是从最小值开始弹出。直到遍历结束,倒顺序返回创建的优先队列中的元素。就可以得到结果。
代码如下:
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) {
// 要统计元素出现频率
unordered_map<int, int> map; // map<nums[i],对应出现的次数>
for (int i = 0; i < nums.size(); i++) {
map[nums[i]]++;
}
// 对频率排序
// 定义一个小顶堆,大小为k
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
// 用固定大小为k的小顶堆,扫面所有频率的数值
for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {
pri_que.push(*it);
if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
pri_que.pop();
}
}
// 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
vector<int> result(k);
for (int i = k - 1; i >= 0; i--) {
result[i] = pri_que.top().first;
pri_que.pop();
}
return result;
}
};
总结: