Files
leetcode-master/problems/0239.滑动窗口最大值.md
youngyangyang04 fd9e7a2d05 Update
2020-08-13 09:15:50 +08:00

4.2 KiB
Raw Blame History

题目地址

https://leetcode-cn.com/problems/sliding-window-maximum/

思路

这是使用单调队列的经典题目。

暴力方法遍历一遍的过程中每次从窗口中在找到最大的数值这样很明显是O(n * k)的算法。

有的同学可能会想用一个大顶堆也就是优先级队列来存放这个窗口里的k个数字这样就可以知道最大的最大值是多少了 但是问题是这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。所以不能用大顶堆。

使用单调队列,即单调递减或单调递增的队列。它不是一个独立的数据结构,需要使用其他数据结构来实现单调队列,例如: dequedeque是双向队列可以选择 从头部或者尾部pop同样也可以从头部或者尾部push。

不要以为实现的单调队列就是 对窗口里面的数进行排序,如果排序的话,那和优先级队列又有什么区别了呢。

使用deque实现的单调队列如下代码详细注释

class MyQueue { //单调队列(从大到小)
public:
    deque<int> que; // 使用deque来实现单调队列
    // 每次弹出的时候比较当前要弹出的数值是否等于队列前端的数值如果相等在pop数据当然也要判断队列当前是否为空。
    void pop(int value) {
        if (!que.empty() && value == que.front()) {
            que.pop_front();
        }
    }
    // 如果push的数值大于后端的数值那么就将队列后端的数值弹出直到push的数值小于等于 队列后端的数值为止。 
    // 然后再将数值push到队列后端这样就保持了队列里的数值是单调从大到小的了。
    void push(int value) {
        while (!que.empty() && value > que.back()) {
            que.pop_back();
        }
        que.push_back(value);

    }
    // 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
    int front() {
        return que.front();
    }
};

动画解释如下:

这样我们就用deque实现了一个单调队列接下来解决滑动窗口最大值的问题就很简单了。

详情看代码吧,已经简洁。

C++代码

class Solution {
public:
    class MyQueue { //单调队列(从大到小)
    public:
        deque<int> que; // 使用deque来实现单调队列
        void pop(int value) {
            if (!que.empty() && value == que.front()) {
                que.pop_front();
            }
        }
        void push(int value) {
            while (!que.empty() && value > que.back()) {
                que.pop_back();
            }
            que.push_back(value);

        }
        int front() {
            return que.front();
        }
    };
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue que;
        vector<int> result;
        for (int i = 0; i < k; i++) { // 先将前k的元素放进队列
            que.push(nums[i]);
        }
        result.push_back(que.front()); // result 记录前k的元素的最大值
        for (int i = k; i < nums.size(); i++) {
            que.pop(nums[i - k]); // 模拟滑动窗口的移动
            que.push(nums[i]); // 模拟滑动窗口的移动
            result.push_back(que.front()); // 记录对应的最大值
        }
        return result;
    }
};

来看一下时间复杂度,时间复杂度: O(n) 有的同学可能想了,在队里中 push元素的过程中还有pop操作呢感觉不是纯粹了O(n)。

其实大家可以自己观察一下单调队列的实现nums 中的每个元素最多也就被 push_back 和 pop_back 一次,没有任何多余操作,所以整体的复杂度还是 O(n)。

空间复杂度因为我们定义一个辅助队列所以是O(k)。

更过算法干货文章持续更新可以微信搜索「代码随想录」第一时间围观关注后回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等就可以获得我多年整理的学习资料。