打卡第十三天,昨天休息,今天继续栈和队列,重新复习了单调队列,上次看ACwing的视频学了单调队列,没有完全学明白,重学之后比之前清晰多了
今日任务
- 滑动窗口最大值
- 347.前 K 个高频元素
- 总结
239. 滑动窗口最大值
给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
我的题解
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int hh = 0, tt = -1;
int q[100010];
vector<int> res;
for(int i = 0; i < nums.size(); i++) {
// 当队列中的容量大于滑动窗口,队头出队列
while(hh <= tt && i - q[hh] + 1 > k) hh++;
// 队尾元素小于要插进来的元素,队尾元素出队列
while(hh <= tt && nums[q[tt]] < nums[i]) tt--;
// 将元素压入队列
q[++tt] = i;
// 当 遍历个数 达到 k 之后,陆续收集结果。
if(i - k >= -1) res.push_back(nums[q[hh]]);
}
return res;
}
};
如果元素先进滑动窗口但是小于后入窗口的,那前面的元素其实毫无作用,因为我们只需要取滑动窗口最大的元素。
使用单调队列,单调队列保存一个单调递减的趋势,保证了队头的元素永远最大,当我们移动滑动窗口,队头元素永远都是当前窗口最大的,单调队列存放的数组下标,因为当滑动窗口移动的时候,我们要把队头元素pop出去,放新的窗口元素。
入队列:当准备入队列的元素大于队尾元素,队尾元素出队列,直到队列为空或者队尾元素大于准备入队列的元素。
出队列:当发现队列的元素长度大于滑动窗口,队头元素出队列。
取窗口最大元素:单调队列里面的元素处于一个单调递减的趋势,队头元素永远处于最大,当取滑动窗口最大元素时候可以直接取队头元素。
代码随想录
class Solution {
private:
class myQueue {
public:
deque<int> que;
void pop(int val) {
if(!que.empty() && val == que.front()) que.pop_front();
}
void push(int val) {
while(!que.empty() && que.back() < val) {
que.pop_back();
}
que.push_back(val);
}
int getMax() {
return que.front();
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
myQueue que;
for(int i = 0; i < k; i++) {
que.push(nums[i]);
}
res.push_back(que.getMax());
for(int i = k; i < nums.size(); i++) {
que.pop(nums[i - k]);
que.push(nums[i]);
res.push_back(que.getMax());
}
return res;
}
};
347.前 K 个高频元素
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按任意顺序
返回答案。
代码随想录
// 时间复杂度:O(nlogk)
// 空间复杂度:O(n)
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;
}
};
看是看明白了,但是写就一言难尽
- 先用unordered_map统计数字出现的次数
- 存入优先队列(小顶堆),将元素 pop 出去,只剩下 k 个就是我们需要的答案。