239. 滑动窗口最大值 (一刷至少需要理解思路)
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值
思路:
今天从这里学到了单调栈的思路以及具体的一些实现。但是仍然有一些问题和思考需要去理解和记住 。
创建一个单调队列, 保证满足队列出口的元素大于等于后进入队列的元素. 这样就一直可以保证滑动窗口中的最大值, 当滑动窗口继续前进时, 我们通过判断滑动窗口丢掉的值是否等于单调队列出口的值来判断是否丢弃最大值. 使用一个数组来存储最大值即可.
1. 单调栈/队列的作用: (为什么要使用单调栈, 如何使用)
我们用滑动窗口遍历整数数组nums, 每次要记录新的滑动窗口的最大值, 这就需要与之前已经遍历过的滑动窗口内的值进行比较, 同时比较每次滑动窗口变化时新加入的值和最大值之间的关系. 使用滑动窗口. 使用单调队列, 我们需要制定相应的规则函数, 比如插入函数, 我们需要考虑插入值大于单调栈里的值时, 把单调栈里的值全部pop出来, 再push这个值就能保证值最大. 理解单调队列的出口值一直是当前滑动窗口的最大值这个概念, 再去编写函数.
2.我们使用deque双向队列容器作为底层容器:
了解deque双向队列容器的一些基本函数:
deque<int> que; | 定义deque容器 |
que.front(); | 获取容器的第一个元素值; |
que.back(); | 获取容器的最后一个元素值; |
que.empty(); | 判断容器是否为空, 返回true或false; |
que.pop_front(); | 删除元素的第一个值, 不返回值; |
que.pop_back(); | 删除元素的最后一个值, 不返回值; |
que.push_front(val); | 从容器前面插入一个值 |
que.push_back(val); | 从容器后面插入一个值 |
que.size(); | 获取容器的大小 |
代码:
class Solution {
public:
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() && val > que.back()){
que.pop_back();
}
que.push_back(val);
}
// 获取当前单调栈的最大值
int getMaxValue(){
return que.front();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;
for(int i = 0; i < k; i++){
que.push(nums[i]);
}
vector<int> res;
res.push_back(que.getMaxValue());
for(int i = k; i < nums.size(); i++){
que.pop(nums[i - k]);
que.push(nums[i]);
res.push_back(que.getMaxValue());
}
return res;
}
};
347.前 K 个高频元素 (一刷至少需要理解思路)
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
思路一:
使用哈希表Map统计数组中各数字出现的次数, 然后通过Vector容器按照次数(Value)排序, 然后再用一个Vector容器存取前K个高频元素的值(Key).思路二:
使用哈希表Map统计数组中各数字出现的次数, 遍历整个整数数组nums的过程中, 使用小顶堆维护一个大小为k的堆(优先队列实现), 最后用Vector容器存取前K个高频元素的值(Key).
代码一:HashMap和排序解法:
class Solution {
public:
// 重载排列顺序
static bool compare(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> num_frequency;
for(int i : nums){
num_frequency[i]++;
}
vector<pair<int, int>> result(num_frequency.begin(), num_frequency.end());
sort(result.begin(), result.end(), compare);
vector<int> res(k);
int cnt = 0;
for(pair<int, int> i : result){
if(cnt < k){
res[cnt] = i.first;
cnt++;
continue;
}
else{
break;
}
}
return res;
}
};
代码二:HashMap和小顶堆解法
class Solution {
public:
//自定义比较函数
class myCompare{
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) {
//HashMap统计出现次数
map<int, int> numfrq;
for(int i : nums){
numfrq[i]++;
}
//优先队列实现小顶堆
priority_queue<pair<int,int> , vector<pair<int,int>>, myCompare> que;
for(pair<int, int> i : numfrq){
que.push(i);
if(que.size() == k && i.second > que.top().second){
que.pop();
}
}
vector<int> res;
//存储前K个频率最高的数字
while(!que.empty()){
pair<int, int> i = que.top();
res.push_back(i.first);
que.pop();
}
return vector<int>(res.begin(), res.begin() + k);
}
};
10/27和10/28两天来理解这两道题, 学到了单调队列和大小顶堆的知识点, 感觉收获满满. 栈的学习告一段落, 认真总结笔记, 继续追赶算法进度.