前置知识
参考前文:
LeetCode刷题笔记【6】:栈&队列专题-1(用栈实现队列, 用队列实现栈)
LeetCode刷题笔记【7】:栈&队列专题-2(有效的括号, 删除字符串的所有相邻重复项, 逆波兰表达式求值)
239. 滑动窗口最大值
题目描述
LeetCode链接:https://leetcode.cn/problems/sliding-window-maximum/
解题思路
思路: 使用deque
实现一个“伪单调队列”;
因为题目要求每次都取这个区间内最大的,所以我们并不需要将区间内所有的数都加入队列并进行排序;
只需要将最有可能成为答案的几个最大数加入备选即可;
并且还需要在窗口滑动的过程中识别被抛出窗口的数,将其移出队列;
具体的操作为:push新数x时,empty则直接push,非empty时,如果队尾数小于x,就pop_back, 直到队尾数大于等于x, 或empty;
pop老数x时, 如果x等于front, 就pop_front, 否则无操作;
代码
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n=nums.size();
vector<int> ans;
deque<int> que;
for(int i=0; i<k; ++i){
while(!que.empty() && que.back()<nums[i])
que.pop_back();
que.push_back(nums[i]);
}
ans.push_back(que.front());
for(int i=0, j=k; j<n; ++i, ++j){
// pop the old num
if(!que.empty() && nums[i]==que.front())
que.pop_front();
// push the new num
while(!que.empty() && que.back()<nums[j])
que.pop_back();
que.push_back(nums[j]);
ans.push_back(que.front());
}
return ans;
}
};
347.前 K 个高频元素
题目描述
LeetCode链接:https://leetcode.cn/problems/top-k-frequent-elements/
快速排序法
思路: 先用unordered_map
记录数字出现的频率;
然后将频率导入vector<pair<int出现次数,int对应数字>> counter
然后使用:
sort(counter.begin(), counter.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
return a.first < b.first;
});
进行从大到小的排序, 然后再将前k
个元素导入vector<int> ans
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> map;
for(int i=0; i<nums.size(); ++i){
map[nums[i]]++;
}
vector<pair<int,int>> counter;
for(auto& element : map){
pair tmp = make_pair(element.second, element.first);
counter.push_back(tmp);
}
sort(counter.begin(), counter.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
return a.first > b.first;
});
vector<int> ans;
for(int i=0; i<k; ++i){
ans.push_back(counter[i].second);
}
return ans;
}
};
优先队列(小根堆)法
思路: 还是先用unordered_map
记录频率, 然后建立一个优先队列(小根堆) priQ
;
保持priQ
中元素数量为k, 如果不足k
, 新元素直接入堆;
如果等于k
, 比较新元素x
和堆顶元素top
;
x<top
则丢弃x
, x>top
则弹出top
并入堆x
;
但是实际操作上, 是直接入堆x
, 然后如果size>k
, 就弹出top
(反正top
是最小的);
class Solution {
public:
class Compare{
public: bool operator()(pair<int,int> a, pair<int,int> b){
return a.second > b.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> map;
for(int i=0; i<nums.size(); ++i){
map[nums[i]]++;
}
priority_queue<pair<int,int>, vector<pair<int,int>>, Compare> priQ;
for(const auto& pair : map){
priQ.push(pair);
if(priQ.size()>k)
priQ.pop();
}
vector<int> ans;
for(int i=0; i<k; ++i){
ans.push_back(priQ.top().first);
priQ.pop();
}
return ans;
}
};
总结
不同的数据结构有适合其解决的典型问题;
一方面很多时候需要用最合适的工具干最合适的事儿, 另一方面当对一部分工具掌握熟练以后, 也可以通过不那么优雅的方式, 解决很多其他问题.
本文参考:
239. 滑动窗口最大值
347.前 K 个高频元素