239. 滑动窗口最大值
一、题目要求
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
进阶:
你能在线性时间复杂度内解决此题吗?
提示:
- 1 <= nums.length <= 10^5
- -10^4 <= nums[i] <= 10^4
- 1 <= k <= nums.length
二、解题思路
分析一下,首先窗口滑动的O(n)是不可避免的,要优化代码的话只能从最大值获取这一块入手,我们知道最大堆(优先队列)能够实现O(1)时间取出最大值,但是窗口滑动时需要进行元素删除(O(k))和插入(O(logk)),所以复杂度还是O(nk)。
逆向思维反推一下,要求线性时间复杂度,而窗口滑动复杂度就是O(n),所以就需要在O(1)时间内获取当前窗口的最大值。想要达到O(1),那就只能使用单调队列的思路了。维护一个单调队列,总是让最大值位于队列末尾,那么就可以o(1)复杂度获取最大值。
所以分析一下,窗口滑动的时候,我们需要进行单调队列内元素的更新:
如果队列头部的元素不再位于当前窗口,就应该删除。
如果窗口内新增的元素大于队列尾部的元素,就可以进行添加。
三、代码实现
class Solution {
private:
class MyQueue { //单调队列(从大到小)
public:
deque<int> que; // 使用deque来实现单调队列
// 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
// 同时pop之前判断队列当前是否为空。
void pop(int value) {
if (!que.empty() && value == que.front()) {
que.pop_front();
}
}
// 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
// 这样就保持了队列里的数值是单调从大到小的了。
void push(int value) {
while (!que.empty() && value > que.back()) {
que.pop_back();
}
que.push_back(value);
}
// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
int front() {
return que.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()); // result 记录前k的元素的最大值
for (int i = k; i < nums.size(); i++) {
que.pop(nums[i - k]); // 滑动窗口移除最前面元素
que.push(nums[i]); // 滑动窗口前加入最后面的元素
result.push_back(que.front()); // 记录对应的最大值
}
return result;
}
};
347.前 K 个高频元素
一、题目要求
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
- 输入: nums = [1,1,1,2,2,3], k = 2
- 输出: [1,2]
示例 2:
- 输入: nums = [1], k = 1
- 输出: [1]
提示:
- 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
- 你的算法的时间复杂度必须优于 $O(n \log n)$ , n 是数组的大小。
- 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
- 你可以按任意顺序返回答案。
二、解题思路
用priority queue和hash map来做。先用map统计每个元素和相应的频率,再把map的entry放入pq中,改造comparable方法使其成为大顶堆,然后pop出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;
for(int i = 0;i<nums.size();i++)
{
map[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,mycomparison> pri_que;
for(unordered_map<int,int>::iterator it=map.begin();it!=map.end();it++)
{
pri_que.push(*it);
if(pri_que.size()>k)
pri_que.pop();
}
vector<int>result;
for(int i = 0;i<k;i++)
{
result.push_back(pri_que.top().first);
pri_que.pop();
}
reverse(result.begin(),result.end());
return result;
}
};