2、滑动窗口的最大值

厨子大约 5 分钟数据结构算法算法基地面试单调队列单调栈滑动窗口双端队列剑指Offer刷题程序厨校招社招

题目描述

剑指 Offer 59 - I. 滑动窗口的最大值open in new window

这个题目,算是很经典的类型,我们的滑动窗口主要分为两种,一种的可变长度的滑动窗口,一种是固定长度的滑动窗口,这个题目算是固定长度的代表。今天我们用双端队列来解决我们这个题目,学会了这个题目的解题思想你可以去解决一下两道题目 剑指Offer 59 - II. 队列的最大值155. leetcode 最小栈,虽然这两个题目和该题类型不同,但是解题思路是一致的,都是很不错的题目,我认为做题,那些考察的很细的,解题思路很难想,即使想到,也不容易完全写出来的题目,才是能够大大提高我们编码能力的题目,希望能和大家一起进步。

题目解析

这个题目我们用到了双端队列,队列里面保存的则为每段滑动窗口的最大值,我给大家做了一个动图,先来看一下代码执行过程吧。

我们先来了解下双端队列吧,队列我们都知道,是先进先出,双端队列呢?既可以从队头出队,也可以从队尾出队,则不用遵循先进先出的规则。

下面我们通过一个动图来了解一下吧。

好啦,我们了解双端队列是什么东东了,下面我们通过一个动画,来看一下代码的执行过程吧,相信各位一下就能够理解啦。

我们就通过题目中的例子来表述。nums = [1,3,-1,-3,5,3,6,7], k = 3

不知道通过上面的例子能不能给各位描述清楚,如果不能的话,我再加把劲,各位看官,请接着往下看。

我们将执行过程进行拆解。

1.想将我们第一个窗口的所有值存入单调双端队列中,单调队列里面的值为单调递减的。如果发现队尾元素小于要加入的元素,则将队尾元素出队,直到队尾元素大于新元素时,再让新元素入队,目的就是维护一个单调递减的队列。

2.我们将第一个窗口的所有值,按照单调队列的规则入队之后,因为队列为单调递减,所以队头元素必为当前窗口的最大值,则将队头元素添加到数组中。

3.移动窗口,判断当前窗口前的元素是否和队头元素相等,如果相等则出队。

4.继续然后按照规则进行入队,维护单调递减队列。

5.每次将队头元素存到返回数组里。

5.返回数组

是不是懂啦,再回去看一遍视频吧。祝大家新年快乐,天天开心呀!

代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int len = nums.length;
        if (len == 0) {
            return nums;
        }
        int[] arr = new int[len - k + 1];
        int arr_index = 0;
        //我们需要维护一个单调递增的双向队列
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < k; i++) {
             while (!deque.isEmpty() && deque.peekLast() < nums[i]) {
               deque.removeLast();
            }
            deque.offerLast(nums[i]);
        }
        arr[arr_index++] = deque.peekFirst();
        for (int j = k; j < len; j++) {
            if (nums[j - k] == deque.peekFirst()) {
                deque.removeFirst();
            }
            while (!deque.isEmpty() && deque.peekLast() < nums[j]) {
                deque.removeLast();
            }
            deque.offerLast(nums[j]);
            arr[arr_index++] = deque.peekFirst();
        }
        return arr;
    }
}

C++ Code:

#include <vector>
#include <deque>
using namespace std;

class Solution {
public:
    vector<int> maxAltitude(vector<int>& nums, int k) {
        int len = nums.size();
        // 边界情况:空数组直接返回空结果
        if (len == 0) {
            return {};
        }
        
        vector<int> arr(len - k + 1); // 结果数组,长度为窗口数量
        int arr_index = 0;
        deque<int> deq; // 双端队列:存储窗口内元素,维护单调递减序列(队首为最大值)
        
        // 处理第一个窗口(前k个元素)
        for (int i = 0; i < k; ++i) {
            // 移除队列中所有比当前元素小的元素(它们不可能成为窗口最大值)
            while (!deq.empty() && deq.back() < nums[i]) {
                deq.pop_back();
            }
            deq.push_back(nums[i]); // 当前元素入队
        }
        // 第一个窗口的最大值为队首元素
        arr[arr_index++] = deq.front();
        
        // 处理后续窗口(从第k个元素开始)
        for (int j = k; j < len; ++j) {
            // 移除窗口左侧的元素(若该元素是当前队列的最大值,则同步从队列移除)
            if (nums[j - k] == deq.front()) {
                deq.pop_front();
            }
            
            // 维护队列的单调递减性:移除所有比当前元素小的队尾元素
            while (!deq.empty() && deq.back() < nums[j]) {
                deq.pop_back();
            }
            deq.push_back(nums[j]); // 当前元素入队
            
            // 当前窗口的最大值为队首元素,存入结果数组
            arr[arr_index++] = deq.front();
        }
        
        return arr;
    }
};