每天学一点,今天解决的是剑指offer的剑指 Offer 59 - I. 滑动窗口的最大值。
输入: 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
可以用暴力法强行破解,但是复杂度无法保证,因此可以考虑滑动窗口,在滑动窗口的思路。
我开始想到每次滑动后都将窗口内的元素进行排序为递减序列,弹出队首作为该滑动窗口的最大值,但是一旦进行排序,就会打乱顺序,那么我怎么保证滑动窗口进行移动时弹出哪个元素?
因此,我们的队列需要满足两个要求
- 队首为该滑动序列的最大值
- 排序后的队列在滑动窗口移动时不需要考虑弹出的元素顺序被打乱。
根据这些特点,单调的双向队列可以满足我们。
其特点为,整个队列是单调递减的,队首元素为当前元素最大值。
为什么需要双向队列?需要对队伍两端进行操作,为什么要对队伍两端进行操作因为
- 队首删除是因为滑动窗口的长度已经过长了,需要对队首元素进行删除操作。
- 队尾删除是因为新插入的元素比队尾的元素大,为了维持队伍的单调性,因此需要删除队尾元素把新插入的元素加上
这样队伍的元素顺序就与所给数组的元素相对顺序相同,且从大到小进行排序,具体到代码如下。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n =nums.size();
if(n<k || k<=0)
return {};//防止传入错误的参数
int low = 1-k,high = 0;
deque<int> dp;
vector<int> res;
while(high < n ){
// 判断滑窗的low是否为最大元素
if(low >= 1 && nums[low -1] == dp[0])/滑动窗口太长了或者左端就是最大元素
dp.pop_front();//删掉队首
while(!dp.empty() && dp[0]<nums[high])
dp.pop_front();//小于nums[high]都删掉
while(!dp.empty() && dp[dp.size()-1]<nums[high])
dp.pop_back();//清空比nums[high]小的元素
dp.push_back(nums[high]);//插入nums[high]
if(low >=0)//如果low>=0说明已经是一个完整的滑动窗口了
res.push_back(dp[0]);//插入队首元素
low++;
high++;
}
return res;
}
};