【剑指 Offer 59 - I. 滑动窗口的最大值 简单】【239. 滑动窗口最大值 困难】【剑指 Offer 59 - II. 队列的最大值 中等】
【剑指 Offer 59 - I. 滑动窗口的最大值 简单】【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]
示例 3:
输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:
输入:nums = [9,11], k = 2
输出:[11]
示例 5:
输入:nums = [4,-2], k = 2
输出:[4]
2. 思路一
利用一个大顶堆可以随时获得堆内元素最大值,随着窗口滑动,会有新的元素进入堆,很容易获得最大值。但是需要注意的是:窗口滑动的过程中,也会有元素从窗口内离开,该元素如果是不是堆顶元素,则可任其放置在堆中,因为不影响获取当前的最大值;如果该元素是堆顶元素,则需将该元素从堆内删除,重新获得堆顶元素,如果堆顶元素还是已经被从窗口移除的元素,应该持续循环删除。那么拿到堆顶元素如何判断它是不是还在窗口内呢?只需需将(元素值,元素索引)同时存入堆即可。
代码
class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { int len = nums.size(); vector<int> res; if(len == 0 || k <= 0) return res; priority_queue<pair<int, int>> q; for(int i = 0; i < k; i++) { q.emplace(nums[i], i); // 原地构造一个元素并插入队列 } res.push_back(q.top().first); for(int i = k; i < len; i++) { q.emplace(nums[i], i); while(q.top().second < i - k + 1) { q.pop(); } res.push_back(q.top().first); } return res; } };
关于C++优先队列的用法,可参考:https://blog.csdn.net/weixin_36888577/article/details/79937886
该解法的最坏时间复杂度为o(n* log(n)),因为如果数组是个递增数组,那么堆的大小也会递增。
解法二:
使用单调队列,思路可参考LeetCode官方题解:
class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int> res; int len = nums.size(); if(len == 0 || k <= 0) return res; deque<int> q; for(int i = 0; i < k; i++) { while(!q.empty() && nums[q.back()] <= nums[i]) { q.pop_back(); } q.push_back(i); } res.push_back(nums[q.front()]); for(int i = k; i < len; i++) { while(!q.empty() && nums[q.back()] <= nums[i]) { q.pop_back(); } q.push_back(i); while(q.front() < i - k +1) q.pop_front(); res.push_back(nums[q.front()]); } return res; } };
【剑指 Offer 59 - II. 队列的最大值 中等】
1. 题目描述:
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:
输入:
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]
2. 思路
借助一个辅助单调队列来记录最大值。队的front始终存储当前队列的最大值。向队列中push时,取辅助队列的队尾元素,如果队尾元素小于当前要push的元素,则将队尾元素从辅助队列中删除,因为当前队尾元素先于当前要push的元素入队(出队时先出队),那么如果当前队尾元素小于要push的元素,那么队列的最大值永远不可能是当前队尾元素
代码:
class MaxQueue { public: queue<int> q; deque<int> auxQueue; MaxQueue() { auxQueue.clear(); } int max_value() { if(auxQueue.empty()) return -1; return auxQueue.front(); } void push_back(int value) { while(!auxQueue.empty() && auxQueue.back() < value) { auxQueue.pop_back(); } auxQueue.push_back(value); q.push(value); } int pop_front() { if(q.empty()) return -1; int res = q.front(); if(res == auxQueue.front()) { auxQueue.pop_front(); } q.pop(); return res; } }; /** * Your MaxQueue object will be instantiated and called as such: * MaxQueue* obj = new MaxQueue(); * int param_1 = obj->max_value(); * obj->push_back(value); * int param_3 = obj->pop_front(); */