滑动窗口动态获取窗口最大值与最小值

介绍滑动窗口的概念及应用,通过双端队列实现动态查找窗口内的最大值,详细解析算法流程与时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

滑动窗口

滑动窗口是一种想象出来的数据结构,窗口有左边界L和右边界R。

在数组或者字符串或者一个序列上,记为S,窗口就是S[L…R]这一部分,L往右滑动意味着有样本滑出窗口,R往右划动意味着有样本滑入窗口,L和R都只能往右滑动。

窗口定义
  1. R边界向右移动,数组中的数从窗口右侧进入窗口
  2. L边界向右移动,数组中的数从窗口左侧出窗口
  3. 左边界不能到右边界的右侧
如何找到动态窗口中的最大值
  1. 准备一个双端队列,数据可以从头部进头部出,也可以从尾部进尾部出。
  2. R++时,将数据从双端队列的尾部插入,此时队列为空,可以直接将数组对应元素的索引直接插入。
  3. 双端队列从头到尾按照从大到小的顺序保持,所以当R++,即窗口右边界往右滑动时,窗口内加入新的数,此时需要判断队列的末端的数字是否大于当前数,如果末端数字大于当前数,因为仍然保持大小顺序,所以可以直接将数插入到队列末端。
  4. 如果队列末端数字索引数组的值小于或等于当前数,若直接插入会违反队列中的大小顺序,所以需要将队列末端的数弹出,直到队列为空(可以直接插入)或者末端的数大于当前数(满足大小顺序)。
  5. L++时,即左边界往右移动,此时需要将队列中那些不在窗口中的数剔除,所以当左边界向右移动时,取出队列的头部元素判断是否等于右边界减去窗口宽度的值(因为此时的左边界是R-W+1),若等于,说明队首元素已经过期,需要弹出。
对比遍历法求最大值,分析算法复杂度

L和R需要滑过整个数组,每个位置最多进入一次双端队列,出去一次(两种情况:一种是当前下标过期,从头部出去;另一种是遇到了大于这个数的数,从尾部弹出)。整个过程的时间复杂度O(N),查询某个时刻最大值O(1)。

题目联系leetcode239

思路:维护一个滑动窗口,将双端队列首部的元素不断弹出到结果数组中

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int R=0;
        int N=nums.size();
        deque<int>dq;
        vector<int>result(N-k+1,0);
        int index=0;
        for(int R=0;R<N;R++){
            while(!dq.empty()&&nums[dq.back()]<=nums[R]){
                dq.pop_back();
            }
            dq.push_back(R);
            if(dq.front()==R-k){
                dq.pop_front();
            }
            if(R>=k-1){
                result[index++]=nums[dq.front()];
            }
        }
        return result;
    }
};
双端队列(deque,全称Double Ended Queue)是一种可以在两端进行插入和删除元素的数据结构,它既支持在头部添加和移除元素,也支持在尾部进行操作。在解决滑动窗口问题时,可以利用这种数据结构来高效地找到给定区间内的最大值最小值。 以下是使用C++的`std::deque`实现滑动窗口最大值最小值的一种常见方法: ```cpp #include <deque> #include <algorithm> // 定义滑动窗口最大值最小值 struct WindowMinMax { std::deque<int> window; // 使用deque作为窗口 int max_val, min_val; void add(int num) { // 添加新元素到窗口 window.push_back(num); // 更新最大值最小值 if (window.size() > 1) { // 窗口大小大于1 max_val = std::max(max_val, num); min_val = std::min(min_val, num); // 如果窗口满,移除左侧的第一个元素并更新最大值最小值 if (window.front() == num - (window.size() - 1)) { window.pop_front(); max_val = std::max(window.front(), max_val); min_val = std::min(window.front(), min_val); } } else { max_val = num; min_val = num; } } void remove(int num) { // 移除指定元素 if (!window.empty() && window.back() == num) { window.pop_back(); if (window.empty()) { max_val = min_val = INT_MIN; // 如果窗口为空,设最大值最小值为整数最小 } else { max_val = *std::max_element(window.begin(), window.end()); min_val = *std::min_element(window.begin(), window.end()); } } } }; int main() { WindowMinMax window; // 示例操作:add(5), add(8), remove(5), add(10), remove(8) window.add(5); // 窗口[5] window.add(8); // 窗口[5, 8] window.remove(5); // 窗口[8] window.add(10); // 窗口[8, 10] window.remove(8); // 窗口[10] // 结果:max_val = 10, min_val = 8 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值