滑动窗口:数据里的 “寻宝小框”,一滑就有大发现!

196 阅读4分钟

image.png 刷短视频时,你有没有想过:要是能快速找到点赞最高的 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);
    }
}

代码解析:

  1. 先初始化窗口大小为 3,计算第一个窗口内的和 windowSum,并把它设为当前最大值 maxSum;
  1. 从第 4 个数开始遍历数组,每一次循环:
    • 把窗口最左边的数 nums[i - 3] 从 windowSum 中减去;
    • 把新进入窗口的数 nums[i] 加到 windowSum 中;
    • 比较 windowSum 和 maxSum,更新 maxSum 为两者中的较大值;
  1. 遍历结束后,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);
    }
}

代码解析:

  1. 用两个指针 left 和 right 表示窗口的左右边界,初始时都指向字符串开头;
  1. 用 charIndexMap 记录每个字符最后一次出现的位置;
  1. 遍历字符串,每次遇到一个字符:
    • 如果字符已经在当前窗口内出现过,把 left 指针移动到该字符上次出现位置的下一位,保证窗口内没有重复字符;
    • 更新字符在 charIndexMap 中的位置;
    • 计算当前窗口的长度 right - left + 1,和 maxLength 比较,更新 maxLength;
  1. 遍历结束后,maxLength 就是最长不含重复字符的子串长度。

滑动窗口的优缺点:寻宝有惊喜,也有小麻烦!

优点

  • 效率高:相比暴力枚举法,滑动窗口能减少重复计算,比如统计连续数据的和时,不用每次都从头加一遍;
  • 灵活:窗口大小可以固定,也可以根据条件动态调整。

缺点

  • 逻辑复杂:遇到复杂问题时,窗口的边界条件、更新规则容易把人绕晕;
  • 适用性有限:不是所有问题都能用滑动窗口解决,比如完全乱序的数据查找。

现在,你是不是已经掌握滑动窗口这个 “寻宝神器” 了?下次遇到需要处理连续数据的问题,不妨试试用它滑动几下,说不定就能快速找到答案!