目录
题目链接:239. 滑动窗口最大值-困难
题目描述:
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1
输出:[1]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
思路:
单调队列
要求最大值,应该保证队列中的数是单调递减的,这样front
才能保证是最大的。
一开始的思路是用单向队列queue
,这样只能push(_back)
跟pop(_front)
。根据窗口滑动,每次pop
窗口里的第一个数,push
最后一个数到队列末尾,然后把队列里最大的数依次求出来即可。但是这样实现每次只能最后一个数跟第一个数比,无法跟中间的数比较,因此行不通。
改用双向队列deque
,思路同上,不过双向队列可以保证队列中的数是单调递减的。
- 假设现在的队列中的数是单调递减的
- 每次遍历到一个新的数
nums[i]
,先判断队列中的第一个数nums[i - k]
是否在该滑动窗口内(这个数一定在上一个滑动窗口内,那么队列中剩下的数一定在该滑动窗口内),若不在先pop
- 将
nums[i]
跟队列里的其他数比,若nums[i]
比队列里最后一个数大,那把最后一个数扔了直至比队列里最后一个数小或者队列空了,这在单向队列里做不到而在双向队列里可以(想象一下,若k
比较大,那么nums[i]
必定在下一个滑动窗口内,且在下一个滑动窗口内跟该滑动窗口nums[i]
前面的数的大小不用再比较了) - 把
nums[i]
加到队列里维持单调递减,此时队列里第一个数则为该滑动窗口最大的数 - 注意:这种解法队列里数的个数始终
≤k
,因此只用一个循环无法判断第一个窗口到哪里结束,因此得先把第一个窗口的数解决掉,再用上述思路从第k
个元素开始遍历。
代码如下:
// 时间复杂度:O(n)
// 空间复杂度:O(k)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
deque<int> dq;
int maxForeK;
for(int i = 0; i < k; ++i){
while((!dq.empty()) && nums[i] > dq.back()){
dq.pop_back();
}
dq.push_back(nums[i]);
}
res.push_back(dq.front());
for(int i = k; i < nums.size(); ++i){
if(!dq.empty() && nums[i - k] == dq.front()){
dq.pop_front();
}
while(!dq.empty() && nums[i] > dq.back()){
dq.pop_back();
}
dq.push_back(nums[i]);
res.push_back(dq.front());
}
return res;
}
};
题目链接:347. 前 K 个高频元素-中等
题目描述:
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的
进阶:你所设计算法的时间复杂度 必须 优于 O(n log n)
,其中 n
是数组大小。
可以直接用unordered_map
保存元素,然后对value
排序
用小顶堆,因为要统计最大前k
个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k
个最大元素。用优先级队列priority_queue
完成小顶堆
注意:
大家对这个比较运算在建堆时是如何应用的,为什么左大于右就会建立小顶堆,反而建立大顶堆比较困惑。
确实 例如我们在写快排的cmp
函数的时候,return left>right
就是从大到小,return left<right
就是从小到大。
优先级队列的定义正好反过来了,可能和优先级队列的源码实现有关(我没有仔细研究),我估计是底层实现上优先队列队首指向后面,队尾指向最前面的缘故!
代码如下:
// 时间复杂度:O(nlogk)
// 空间复杂度:O(n)
class Solution {
public:
// 小顶堆,仿函数
struct mycomparison {
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> umap;
vector<int> res;
for(int i = 0; i < nums.size(); ++i){
++umap[nums[i]];
}
// 定义一个小顶堆,元素为umap的键值对
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
for(auto& it : umap){
pri_que.push(it);
if(pri_que.size() > k) // 保证堆的大小一直为k
pri_que.pop();
}
// 题目无要求按顺序,可以直接输出一个pop一个
// 找出前K个高频元素,小顶堆先弹出的是最小的,可以for循环倒序来输出到数组
while(!pri_que.empty()){
res.emplace_back(pri_que.top().first);
pri_que.pop();
}
return res;
}
};
标签:窗口,nums,队列,随想录,back,347,239,滑动,dq
From: https://www.cnblogs.com/lurk3r/p/18053036