[Leetcode]双项队列解决滑动窗口最大值难题

这道题是从优先队列的难题里面找到的一个题目。但是解法并非优先队列,而是双项队列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。难点就在于,如何把这个不在区间范围内的数字移除。容器

However

在这里我介绍一种更快更通用的方法,使用双项队列。这里为了效率和快捷使用了deque容器,也可使用list容器。

我声明了一个变量deque<int>window,用于存储下标。这个变量有如下特色:

  1. 变量的最前端(也就是window.front())是这次遍历的最大值的下标
  2. 当咱们遇到新的数时,将新的数和双项队列的末尾(也就是window.back())比较,若是末尾比新数小,则把末尾扔掉,直到该队列的末尾比新数大或者队列为空的时候才中止,作法有点像使用栈进行括号匹配。
  3. 双项队列中的全部值都要在窗口范围内

特色一只是方便咱们获取每次窗口滑动一格以后的最大值,咱们能够直接经过window.front()得到

经过特色二,能够保证队列里的元素是从头至尾降序的,因为队列里只有窗口内的数,因此他们其实就是窗口内第一大,第二大,第三大...的数。

特色三就是根据题意设置的。但咱们实际上只用比较如今的下标和window.front()就能够了,想一想为何?

Answer: 由于只要窗口内第一大元素也就是这个window.front()在窗口内,那咱们能够不用管第二大第三大元素在不在区间内了。由于答案必定是这个第一大元素。若是window.front()不在窗口内,则将其弹出,第二个大元素变成第一大元素,第三大元素变成第二大元素以此类推。

代码编写的过程还要时刻检查队列是否为空防止抛出异常。

根据上面这些信息咱们就能够编写此题的代码了。

Solution

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;
    }
};
相关文章
相关标签/搜索