刷短视频时,你有没有想过:要是能快速找到点赞最高的 10 秒片段该多好?玩游戏时,怎么实时计算最近 5 次攻击的平均伤害?这些问题,用编程界的 “寻宝神器”—— 滑动窗口,都能轻松解决!今天咱们就用最接地气的方式,带大家揭开它的神秘面纱!
什么是滑动窗口?像贪吃蛇一样 “吃” 数据!
先讲个生活小例子:你在超市排队结账,收银员每扫描 5 件商品就计算一次总价。这个 “每次扫描 5 件商品” 的过程,就是滑动窗口的雏形!
在编程里,滑动窗口就像一个可以伸缩的小框,套在一串数据(比如数组、字符串)上。它会按照规则在数据上 “滑动”,每次滑动时,框内的数据会发生变化,我们可以对框内的数据进行统计、查找等操作。就像贪吃蛇游戏里,蛇头不断前进,“吃掉” 新食物的同时,身后的尾巴也会缩短,始终保持身体长度固定,滑动窗口也是如此!
滑动窗口能做什么?从找单词到算销量,样样行!
滑动窗口的应用超广泛,比如:
- 找字符串里的特定单词:在一篇超长文章里,快速找到连续包含 “Java” 和 “编程” 的最短句子;
- 统计游戏数据:实时计算玩家最近 10 次操作的得分平均值;
- 电商数据分析:统计最近 7 天的商品销量峰值。
接下来,我们用 Java 代码实战,看看滑动窗口到底怎么用!
实战 1:找到数组中连续 3 个数的最大和
假设我们有一个数组 [1, 3, -1, -3, 5, 3, 6, 7],现在要用滑动窗口找到连续 3 个数的最大和。
public class SlidingWindowExample {
public static int maxSumOfThree(int[] nums) {
if (nums.length < 3) {
throw new IllegalArgumentException("数组长度至少为3");
}
int windowSum = nums[0] + nums[1] + nums[2];
int maxSum = windowSum;
for (int i = 3; i < nums.length; i++) {
// 滑动窗口:去掉最左边的数,加上新进来的数
windowSum = windowSum - nums[i - 3] + nums[i];
maxSum = Math.max(maxSum, windowSum);
}
return maxSum;
}
public static void main(String[] args) {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int result = maxSumOfThree(nums);
System.out.println("连续3个数的最大和是:" + result);
}
}
代码解析:
- 先初始化窗口大小为 3,计算第一个窗口内的和 windowSum,并把它设为当前最大值 maxSum;
- 从第 4 个数开始遍历数组,每一次循环:
-
- 把窗口最左边的数 nums[i - 3] 从 windowSum 中减去;
-
- 把新进入窗口的数 nums[i] 加到 windowSum 中;
-
- 比较 windowSum 和 maxSum,更新 maxSum 为两者中的较大值;
- 遍历结束后,maxSum 就是连续 3 个数的最大和。
实战 2:在字符串中找不含重复字符的最长子串
比如输入字符串 abcabcbb,最长不含重复字符的子串是 abc,长度为 3。
import java.util.HashMap;
import java.util.Map;
public class LongestSubstringWithoutRepeating {
public static int lengthOfLongestSubstring(String s) {
int maxLength = 0;
int left = 0;
Map<Character, Integer> charIndexMap = new HashMap<>();
for (int right = 0; right < s.length(); right++) {
char currentChar = s.charAt(right);
// 如果字符在窗口内出现过,更新左边界
if (charIndexMap.containsKey(currentChar)) {
left = Math.max(left, charIndexMap.get(currentChar) + 1);
}
// 更新字符最新出现的位置
charIndexMap.put(currentChar, right);
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
public static void main(String[] args) {
String s = "abcabcbb";
int result = lengthOfLongestSubstring(s);
System.out.println("最长不含重复字符的子串长度是:" + result);
}
}
代码解析:
- 用两个指针 left 和 right 表示窗口的左右边界,初始时都指向字符串开头;
- 用 charIndexMap 记录每个字符最后一次出现的位置;
- 遍历字符串,每次遇到一个字符:
-
- 如果字符已经在当前窗口内出现过,把 left 指针移动到该字符上次出现位置的下一位,保证窗口内没有重复字符;
-
- 更新字符在 charIndexMap 中的位置;
-
- 计算当前窗口的长度 right - left + 1,和 maxLength 比较,更新 maxLength;
- 遍历结束后,maxLength 就是最长不含重复字符的子串长度。
滑动窗口的优缺点:寻宝有惊喜,也有小麻烦!
优点:
- 效率高:相比暴力枚举法,滑动窗口能减少重复计算,比如统计连续数据的和时,不用每次都从头加一遍;
- 灵活:窗口大小可以固定,也可以根据条件动态调整。
缺点:
- 逻辑复杂:遇到复杂问题时,窗口的边界条件、更新规则容易把人绕晕;
- 适用性有限:不是所有问题都能用滑动窗口解决,比如完全乱序的数据查找。
现在,你是不是已经掌握滑动窗口这个 “寻宝神器” 了?下次遇到需要处理连续数据的问题,不妨试试用它滑动几下,说不定就能快速找到答案!