滑动窗口

滑动窗口(Sliding Window)是一种用于处理数组/字符串子区间问题的高效算法。它通过维护一个动态的“窗口”,在遍历数据时调整窗口的起始和结束位置,从而避免重复计算,降低时间复杂度。
背景问题
给定一个长度为 n
的整数数组和一个固定窗口大小 k
,我们需要找到所有长度为 k
的连续子数组,并计算它们的和,最终返回最大的那个和。
例如,对于下面的输入数据:
第一行包括两个整数,第一个 n
为 6,表示接下来要输入 6 个整数(第二行的整数),第一行中的第二个数字 k
为 3,表示要求大小为 3 的子数组的最大数字和。
这里大小为 3 的子数组(所有可能的窗口)有:
[2 1 5]
[2 5 1]
[5 1 3]
,求出的数字和为 9 ,最大
[1 3 2]
这几个子数组中,数字和最大的为 9,由 [5 1 3]
这三个数字求和得到。
朴素解法(暴力求解)
同样的,我们可以用枚举的方法去实现,从左至右,依次选一个数字,然后接下来连续的三个数字之和,再选下一个数字,连续三个数字求和,在所有的数字和中选取最大值,即是结果。
参考代码如下:
#include <bits/stdc++.h>
using namespace std;
int n, k, mx, q[110];
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> q[i];
// 枚举去求最一个 k 子数组的数字和
for (int i = 0; i < n - k + 1; i++) {
int sum = 0;
for (int j = i; j < i + k; j++) {
sum += q[j];
}
// 找子数组数字和的最大值
if (sum > mx) mx = sum;
}
cout << mx << endl;
return 0;
}
朴素解法的时间复杂度为 O(n×k)。
滑动窗口法求解
我们可以利用滑动窗口技巧来优化,更高效的解决此类问题。
顾名思义,这里的连续 3 个元素的一组数就是一个“窗口”,这个“窗口”,是在不断变化的(向后滑动)。
滑动窗口又分为固定大小与可变大小两种类型的窗口,我们这里是固定大小窗口。
如果用滑动窗口来求解,关键步骤为:
- 先计算第一个窗口的和
- 后续窗口的和 = 前一个窗口的和 - 左边界离开的元素 + 右边界新进入的元素
- 窗口继续后移,不断更新最大值

参考代码如下:
#include <bits/stdc++.h>
using namespace std;
int n, k, mx, q[110];
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> q[i];
int window_sum = 0;
for (int i = 0; i < k; i++) {
window_sum += q[i];
}
int mx_sum = window_sum;
// 滑动窗口
for (int right = k; right < n; right++) {
int left = right - k; // 左边界
window_sum = window_sum - q[left] + q[right]; // 更新窗口数字和
mx_sum = max(mx_sum, window_sum); // 更新最大值
}
cout << mx_sum << endl;
return 0;
}
可以发现,使用滑动窗口技巧来解决这个问题,每个元素只被访问两次(进入窗口一次,离开窗口一次),时间复杂度降为 O(n);
滑动窗口代码框架
【TODO: 补充】
滑动窗口的应用场景
滑动窗口适用于:
- 连续子数组/子字符串问题。
- 求最大/最小满足条件的窗口。
- 数据是线性结构(数组、字符串、链表)。
总结
使用滑动窗口可以将时间复杂度从暴力求解的 O(n2) 优化到 O(n),因为每个元素最多被访问两次。
大家尽量多做相关的典型题目,在做的过程中再去理解框架,再尝试修改条件(如求最大值/最小值)。