239. Sliding Window Maximum
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
For example,
Given nums = [1,3,-1,-3,5,3,6,7], and k = 3.
Window position Max
--------------- -----
[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
Therefore, return the max sliding window as [3,3,5,5,6,7].
Note:
You may assume k is always valid, ie: 1 ≤ k ≤ input array's size for non-empty array.
Follow up:
Could you solve it in linear time?
这个是一个单调队列的问题,每一次需要保存窗口最大值的大小,然后得到窗口的最大值,若数组大小为n,对于窗口大小为K,最多有n-k + 1 个最大值。
算法思想
1、利用最大堆思想,遍历线性表,每一个进行一个维护堆的操作,当堆的大小为k时,进行出堆操作,然后再将最早进堆的元素删除,遍历一次可以求得解。
时间复杂度 O(nlongk) 空间复杂度O(k)
2、利用单调队列的思想。(所谓的单调队列就是指在确保队列的特性(先进先出)的前提下还需要维护队列的最大/最小值)当窗口等于K时,取得最大值后出队。
时间复杂度为O(n) 空间复杂度为O(k)
详细步骤
1、实现第一种思想,可以利用STL中的map,map自带升序/降序
2、这里详细说一下第二种思想,利用STL中deque进行辅助操作,队列不是真实存在的。
利用deque来保存最大值下标(这里假设deque的最后一个元素就为最大值元素的下标),那可以得出这样一个结论,当遍历至下标i时,只需要判断nums[i]与nums[back]的大小,因为nums[back]是前k-1个最大的元素(back为deque最后一个元素)
得到上面的结论,那么只需要维护deque最后一个元素是前k-1个元素最大元素的下标即可。
1. 当deque为空时,就直接入队
2. 当deque不为空时,如果当前元素,大于保存元素的值,将所有在队列中小于当前元素的值删除掉,然后当前元素入队
3. 在判断一下队头的元素是不是在窗口之内,若不在就删除掉。
利用map
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ret;
map<int,int> mymap;
for(int i = 0 ; i < nums.size() ; ++i){
++mymap[nums[i]];
if(i >= k - 1){
ret.push_back(mymap.rbegin()->first);
--mymap[nums[i-k+1]];
if(mymap[nums[i-k+1]] == 0)mymap.erase(nums[i-k+1]);
}
}
return ret;
}
利用deque
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> d;
vector<int> ret;
for (int i = 0; i < nums.size(); ++i) {
while (!d.empty() && nums[d.back()] < nums[i]) d.pop_back();//当队不为空时并且将小于当前元素的所有值出队
d.push_back(i);
if (i>=k-1) ret.push_back(nums[d.front()]);//队的长度等于k时入队
if(d.front() <= i-k+1)//判断一下队头的元素是不是在窗口之内
d.pop_front();
}
return ret;
}