LeetCode239.滑动窗口最大值

239.滑动窗口最大值

文章目录

      • 239.滑动窗口最大值
        • 一、题目
        • 二、题解
          • 算法思路
          • 具体实现
          • 算法分析
        • 拓展知识:滑动窗口(内含第二种版本的题解,也是很聪明的办法)
          • 示例代码


一、题目

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

输入: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

示例 2:

输入:nums = [1], k = 1
输出:[1]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

二、题解

算法思路

该算法使用双端队列来解决滑动窗口中的最大值问题。通过维护一个单调递减的队列,队列中的元素按照从大到小的顺序排列,且队列的前端元素始终是当前滑动窗口的最大值。在遍历数组过程中,不断地调整队列中的元素,以保持队列的单调性,并将当前窗口的最大值加入结果数组。

具体实现
  1. 定义一个内部类Myqueue,用于实现单调递减的队列。该类内部维护一个双端队列(deque)作为队列容器。
  2. Myqueue类中的push函数用于将元素加入队列。在加入元素前,首先从队尾开始,将小于当前元素的所有元素移除,以保持队列的单调性。然后将当前元素加入队尾。
  3. Myqueue类中的pop函数用于移除队列中的元素。该函数接收一个参数val,用于判断是否需要移除队列中的元素。如果队列不为空且队头元素等于val,则移除队头元素。
  4. Myqueue类中的get_front函数用于获取队列的前端元素,即当前滑动窗口的最大值。
  5. maxSlidingWindow函数中,创建Myqueue的实例myque用于维护滑动窗口的最大值。
  6. 初始化阶段,将前k个元素依次加入myque中,并将滑动窗口的最大值加入结果数组。
  7. k开始遍历数组,每次移动滑动窗口,先移除窗口左侧的元素,然后将新的元素加入myque中,并将当前滑动窗口的最大值加入结果数组。
  8. 返回结果数组。
class Solution{
public:
    class Myqueue{
        public:
            deque<int> que;
            void pop(int val){
                if(!que.empty() && val == que.front()){
                    que.pop_front();
                }        
            }
            void push(int x){
                while(!que.empty() && que.back() < x){
                    que.pop_back();
                }
                que.push_back(x);
            }
            
            int get_front(){
                return que.front(); 
            }
    };
    public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k){
        vector<int> result;
        Myqueue myque;
        //初始化
        for(int i = 0; i < k; i++){
            myque.push(nums[i]);
        }
        result.push_back(myque.get_front());
        for(int i = k; i < nums.size(); i++){
            myque.pop(nums[i - k]);
            myque.push(nums[i]);
            result.push_back(myque.get_front());
        }
        return result;
    }
};
算法分析
  • 时间复杂度分析:该算法的时间复杂度为O(N),其中N为数组的长度。因为我们需要遍历整个数组,并在每个位置上进行一些入队和出队的操作。
  • 空间复杂度分析:该算法的空间复杂度为O(K),其中K为滑动窗口的大小。因为我们需要维护一个大小为K的双端队列。

通过使用双端队列来维护滑动窗口的最大值,该算法能够高效地解决滑动窗口问题,具有较好的时间和空间复杂度。

拓展知识:滑动窗口(内含第二种版本的题解,也是很聪明的办法)

滑动窗口算法的基本思想是维护一个窗口,通过移动窗口的起始位置和结束位置,来寻找满足特定条件的子序列或子串。在每次窗口移动的过程中,我们可以通过添加或移除元素来更新窗口的状态,以满足问题的要求。

滑动窗口算法的一般步骤如下:

1.初始化窗口的起始位置和结束位置。
2.在给定的条件下,扩展窗口的结束位置,直到窗口中的元素满足特定要求。
3.更新窗口的状态,记录满足条件的子序列或子串。
4.缩小窗口的起始位置,继续寻找下一个满足条件的子序列或子串。
5.重复步骤2至4,直到遍历完整个数组或字符串。

滑动窗口算法常用于解决如下类型的问题:

  • 寻找最长或最短的连续子数组或子字符串,满足特定的条件。
  • 计算满足特定条件的子数组或子字符串的个数。
  • 寻找满足特定条件的子数组或子字符串的最大或最小值。
示例代码

下面是使用C++实现滑动窗口最大值问题的示例代码,该代码使用了双端队列来维护滑动窗口的最大值:

#include 
#include 
#include 

using namespace std;

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    vector<int> result;
    deque<int> dq;

    for (int i = 0; i < nums.size(); i++) {
        // 移除窗口左侧的元素
        if (!dq.empty() && dq.front() == i - k) {
            dq.pop_front();
        }

        // 保持队列的单调性,将小于当前元素的元素移除
        while (!dq.empty() && nums[dq.back()] < nums[i]) {
            dq.pop_back();
        }

        // 将当前元素加入队列
        dq.push_back(i);

        // 窗口满足大小为k时,记录当前窗口的最大值
        if (i >= k - 1) {
            result.push_back(nums[dq.front()]);
        }
    }

    return result;
}

int main() {
    vector<int> nums = {1, 3, -1, -3, 5, 3, 6, 7};
    int k = 3;
    vector<int> result = maxSlidingWindow(nums, k);

    cout << "滑动窗口中的最大值:" << endl;
    for (int i = 0; i < result.size(); i++) {
        cout << result[i] << " ";
    }
    cout << endl;

    return 0;
}

在上述示例代码中,我们使用deque来实现双端队列,其中dq.front()表示队列的前端元素,dq.back()表示队列的后端元素。通过遍历数组并在每个位置上进行入队和出队的操作,我们可以找到滑动窗口中的最大值,并将其存储在result向量中。

你可能感兴趣的:(Leetcode刷题,算法,栈和队列)