滑动窗口

滑动窗口算法是一种处理序列数据(如数组、字符串等)的高效方法,它通过在数据上移动一个固定大小的窗口,并在每个窗口位置上执行特定的操作来解决问题。滑动窗口算法的原理和操作步骤如下:

算法原理

  1. 窗口初始化:定义一个长度固定的窗口,这个窗口在序列的起始位置开始。

  2. 窗口操作:对当前窗口内的元素执行所需的操作,这可能包括计算和、最大值、最小值、计数等。

  3. 窗口滑动:将窗口向前移动一位,通常是向右移动一个元素。这可以通过简单地扩展窗口的右边界并缩小左边界来实现。

  4. 重复操作:重复步骤2和3,直到窗口的右边界到达序列的末尾。

  5. 结果输出:在每次窗口移动后,根据需要输出结果,或者在窗口移动完成后输出最终结果。

操作步骤

  1. 确定窗口大小:根据问题的需求确定窗口的大小。窗口大小可能是固定的,也可能是根据某些条件动态变化的。

  2. 初始化数据结构:创建用于存储窗口边界、临时结果等的数据结构。

  3. 执行窗口内操作:对当前窗口内的元素执行操作,如计算窗口内元素的和、找到窗口内的最大/最小值等。

  4. 更新窗口:根据滑动窗口算法的类型(例如,增加窗口的右边界、减少窗口的左边界等),更新窗口的位置。

  5. 处理边界情况:在窗口移动过程中,需要注意边界情况,如窗口左边界超出序列起始位置或右边界超出序列末尾位置。

  6. 记录结果:在每次窗口移动后,记录或输出当前窗口的操作结果。

  7. 结束条件:当窗口的右边界到达序列末尾时,算法结束。

滑动窗口算法在很多问题中都非常有用,例如计算连续子数组的最大和、找到无重复字符的最长子串、解决矩阵中的最小路径和问题等。通过在序列上滑动窗口并执行操作,我们可以在一次遍历中解决问题,避免了多次遍历序列的开销,从而提高了算法的效率。

问题:

给定一个长度不超过10^6的数组,你需要找到一个大小为m的滑动窗口,这个窗口会从数组的最左端滑动到最右端,每次只向右移动一个位置。在窗口的每次移动中,你只能看到窗口内的k个数字。目标是找出在每次移动过程中,这m个数字的某种特定属性(最小值、最大值),并对这些属性进行记录或输出。

输入格式

  • 第一行包含两个整数 n 和 k,用空格隔开。
    • n 是数组的长度。
    • k 是滑动窗口的长度。
  • 第二行包含 n 个整数,这些整数用空格隔开,代表数组的具体数值。

输出格式

  • 输出分为两行。
  • 第一行:从左至右,输出每个位置滑动窗口中的最小值。
  • 第二行:从左至右,输出每个位置滑动窗口中的最大值。

示例

输入样例:
10 3
5 1 4 2 8 7 3 1 5 9
输出样例:
1 1 2 2 3 1 1 1 
5 4 8 8 8 7 5 9 

代码:

#include<iostream> // 引入标准输入输出库
using namespace std; // 使用标准命名空间

const int N = 1000010; // 定义常量N,表示数组的最大容量

int a[N], q[N]; // 定义两个数组a和q,分别用于存储输入的数组和辅助数组

int main()
{
    int n, m; // 定义变量n和m,分别表示数组a的长度和窗口大小
    cin >> n >> m; // 从标准输入读取n和m的值
    for(int i = 0; i < n; i ++) // 循环读取n个整数到数组a中
        cin >> a[i];
    
    int hh = 0, tt = -1; // 定义两个指针hh和tt,分别表示q数组的头部和尾部
    // 第一部分:找出位于最小值之间的元素并输出
    for(int i = 0; i < n; i ++)
    {
        // 如果当前位置减去q数组头部位置大于窗口大小m,则移动头部指针
        if(i - q[hh] + 1 > m ) hh ++;
        // 移除q数组尾部比当前元素大的所有元素
        while(hh <= tt && a[i] < a[q[tt]])
            tt --;
        // 将当前元素的位置i添加到q数组尾部
        q[++ tt] = i;
        // 如果当前位置加上1等于或超过窗口大小m,则输出q数组头部的元素
        if(i + 1 >= m) 
            cout << a[q[hh]] << " ";
    }
    puts(""); // 输出换行符
    
    // 重置指针hh和tt
    hh = 0, tt = -1;
    // 第二部分:找出位于最大值之间的元素并输出
    for(int i = 0; i < n; i ++)
    {
        // 如果当前位置减去q数组头部位置大于窗口大小m,则移动头部指针
        if(i - q[hh] + 1 > m ) hh ++;
        // 移除q数组尾部比当前元素小的所有元素
        while(hh <= tt && a[i] > a[q[tt]])
            tt --;
        // 将当前元素的位置i添加到q数组尾部
        q[++ tt] = i;
        // 如果当前位置加上1等于或超过窗口大小m,则输出q数组头部的元素
        if(i + 1 >= m) 
            cout << a[q[hh]] << " ";
    }
    return 0; // 程序结束
}

 滑动窗口算法是一种高效的序列处理技术,通过在数据序列上移动一个固定大小的窗口来解决问题。它涉及初始化窗口、计算窗口内元素的特定属性、随着窗口的滑动更新这些属性,并在适当的时候输出结果。这种方法适用于统计分析、数据流处理、子数组搜索等多种场景,能够在一次遍历中解决问题,从而优化时间和空间效率。在实现时,需要仔细处理边界情况并选择合适的数据结构以维护窗口状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值