这道题是从优先队列的难题里面找到的一个题目。但是解法并非优先队列,而是双项队列deque
前端
其实只要知道思路,这一道题直接写没有太大的问题。咱们看看题数组
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只能够看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。翻译
返回滑动窗口最大值。code
示例:队列
输入: 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个最大值it
固然暴力naive的解法就显而易见了,用O(n*k)能够解决,不细说。但仍是手痒写个伪代码:io
for(size_t i=0;i<nums.size();i++){ int maxValue=INT_MIN; for(size_t j=i;j<i+k;j++){ maxValue=max(maxValue,nums[j]); } res.push_back(maxValue); }
我想到的另外一种方法是创建一个最大堆。class
想法很简单:每次读入一个新的数字,就把不在区间范围内的数字移除堆里,而后堆的顶部就是这次循环的结果。效率
一个堆其实很是容易实现。咱们甚至可使用一个C++ STL
中的priority_queue
。难点就在于,如何把这个不在区间范围内的数字移除。容器
在这里我介绍一种更快更通用的方法,使用双项队列。这里为了效率和快捷使用了deque
容器,也可使用list
容器。
我声明了一个变量deque<int>window
,用于存储下标。这个变量有如下特色:
window.front()
)是这次遍历的最大值的下标window.back()
)比较,若是末尾比新数小,则把末尾扔掉,直到该队列的末尾比新数大或者队列为空的时候才中止,作法有点像使用栈进行括号匹配。特色一只是方便咱们获取每次窗口滑动一格以后的最大值,咱们能够直接经过window.front()
得到
经过特色二,能够保证队列里的元素是从头至尾降序的,因为队列里只有窗口内的数,因此他们其实就是窗口内第一大,第二大,第三大...的数。
特色三就是根据题意设置的。但咱们实际上只用比较如今的下标和window.front()
就能够了,想一想为何?
Answer: 由于只要窗口内第一大元素也就是这个window.front()
在窗口内,那咱们能够不用管第二大第三大元素在不在区间内了。由于答案必定是这个第一大元素。若是window.front()
不在窗口内,则将其弹出,第二个大元素变成第一大元素,第三大元素变成第二大元素以此类推。
代码编写的过程还要时刻检查队列是否为空防止抛出异常。
根据上面这些信息咱们就能够编写此题的代码了。
class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { if(k==0)return {}; vector<int>res; deque<size_t>window; /*Init K integers in the list*/ for (size_t i = 0; i < k; i++) { while (!window.empty() && nums[i] > nums[window.back()]) { window.pop_back(); } window.push_back(i); } res.push_back(nums[window.front()]); /*End of initialization*/ for (size_t i = k; i < nums.size(); i++) { if (!window.empty() && window.front() <= i - k) { window.pop_front(); } while (!window.empty() && nums[i] > nums[window.back()]) { window.pop_back(); } window.push_back(i); res.push_back(nums[window.front()]); } return res; } };