给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只能够看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。web
返回滑动窗口最大值。数组
示例:svg
输入: 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 注意: 你能够假设 k 老是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。
进阶:
你能在线性时间复杂度内解决此题吗?
思路分析:使用一个队列做为窗口,顺序储存k个元素。
使用一个双端队列,维持双端队列的递增有序,(队尾为窗口中的最大值)。优化
首先窗口队列的队头出队,以便放入新的元素。 出队的同时判断出队的元素是否与双端队列的最大值相同,若是相同,双端队列的最大值也须要出队尾 放入一个元素到窗口的队尾。 入队后将双端队列头部小于刚刚放入的值的元素出队,若是刚刚放入的元素值大于等于当前双端队列的队尾,则这个元素也须要放置到双端队列的队尾 不然,放入双端队列的对头。(此时双端队列仍然递增有序)
class Solution { deque<int> maxEleDeque;//队尾放置当前窗口中的最大值(始终保持递增有序 queue<int> windowQue;//顺序储存k个元素 public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int> result; int numsSize = nums.size(), tempValue; if (numsSize == 0) { return result; } //第一步:肯定第一个窗口,将前k个值放入队列中 for (int i = 0; i < k; ++i) { windowQue.push(nums[i]);//放入窗口 //若是放入的值会大于双端队列中的队尾,则双端队列的队尾出队 while (!maxEleDeque.empty() && maxEleDeque.front() < nums[i]) { maxEleDeque.pop_front(); } //若是当前放入的值大于、等于双端队列的队尾,那么这个值要放入双端队列的对头(维持双端队头为当前窗口中的最大值 if (maxEleDeque.empty() || nums[i] >= maxEleDeque.back()) { maxEleDeque.push_back(nums[i]); } else {//不然放入双端队列尾部(因为上面while循环去除了小于nums[i]的值,因此放入以后双端队列任然有序递增 maxEleDeque.push_front(nums[i]); } } result.push_back(maxEleDeque.back());//找到的第一个窗口 //第二步:不断将窗口后移,窗口头部出队,窗口后部入队 for (int i = k; i < numsSize; ++i) { //第一小步:窗口队列先出队头 tempValue = windowQue.front(); windowQue.pop(); //若是出队的值与最大值相等(maxEleDeque队尾 if (maxEleDeque.back() == tempValue) { maxEleDeque.pop_back();//最大值也须要出队 } //第二小步:将nums[i]放入窗口队列 windowQue.push(nums[i]); //若是放入的值会大于双端队列中的队尾,则双端队列的队尾出队 while (!maxEleDeque.empty() && maxEleDeque.front() < nums[i]) { maxEleDeque.pop_front(); } //下面维持双端队列的递增有序 //第三小步:将双端队列的头部小于nums[i]的元素出队 //若是当前放入的值大于、等于栈顶,那么这个值要放入队尾(维持队尾为当前队列中的最大值 if (maxEleDeque.empty() || nums[i] >= maxEleDeque.back()) { maxEleDeque.push_back(nums[i]); } else {//不然放入双端队列尾部(因为上面while循环去除了小于nums[i]的值,因此放入以后双端队列任然有序递增 maxEleDeque.push_front(nums[i]); } result.push_back(maxEleDeque.back());//当前窗口队列的最大值 } return result; } };
观察发现,窗口队列有些多余,下面将把窗口队列去掉,将第一个窗口的肯定、窗口的移动进行合并,进行优化。code
class Solution { deque<int> maxEleDeque;//队尾放置当前窗口中的最大值(始终保持递增有序 public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int> result; int numsSize = nums.size(), tempValue; if (numsSize == 0) { return result; } //将寻找一个窗口、窗口的移动合并 for (int i = 0; i < numsSize; ++i) { //去掉窗口队列的出队、入队操做 //只有当窗口中的元素数量等于k,而且出队的元素(nums[i - k])等于队尾最大值时才须要先出队,后入队 if (i >= k && maxEleDeque.back() == nums[i - k]) { maxEleDeque.pop_back();//最大值也须要出队 } //若是放入的值(nums[i])会大于双端队列中的队尾,则双端队列的队尾出队 while (!maxEleDeque.empty() && maxEleDeque.front() < nums[i]) { maxEleDeque.pop_front(); } //下面维持双端队列的递增有序 //第三小步:将双端队列的头部小于nums[i]的元素出队 //若是当前放入的值(nums[i])大于、等于队头,那么这个值也要放入队尾(维持栈顶为当前队列中的最大值 if (maxEleDeque.empty() || nums[i] >= maxEleDeque.back()) { maxEleDeque.push_back(nums[i]); } else {//不然放入双端队列头部(因为上面while循环去除了小于nums[i]的值,因此放入以后双端队列任然有序递增 maxEleDeque.push_front(nums[i]); } //只有当窗口中的元素数等于k,才须要寻找最大值 if (i >= k - 1) { result.push_back(maxEleDeque.back());//当前窗口队列的最大值 } } return result; } };