从生活案例理解滑动窗口最大值:一个超直观的思路讲解|LeetCode 239 滑动窗口最大值

LeetCode 239 滑动窗口最大值

点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中)
更多干货,请关注公众号【忍者算法】,回复【刷题清单】获取完整题解目录~

用生活中的例子来理解

想象你是一位摄影师,在拍摄一场马拉松比赛。你的相机一次只能拍摄3个跑步者(就像一个宽度为3的窗口)。随着比赛进行,你的镜头不断向前移动,每次只移动一点点,要在每张照片中找出最高的那位跑步者的身高。这就是我们今天要解决的"滑动窗口最大值"问题。

问题是什么

LeetCode第239题给我们一个任务:假设有一个数组,比如 [2,3,4,2,6,2,5,1],然后给我们一个宽度为3的窗口。这个窗口会从左往右滑动,每次移动一格。我们需要记录下每个窗口中的最大值。

举个简单的例子:

数组:[2,3,4]  窗口大小:2

第一个窗口:[2,3] 4   最大值是3
第二个窗口:2 [3,4]   最大值是4

所以最终结果是:[3,4]

最简单的解决方案:看一看比一比

就像我们肉眼看照片找最高的人一样,最简单的方法就是每次都看窗口里的所有数字,找出最大的那个。

// 这是最容易理解的方法
public int[] maxSlidingWindow(int[] nums, int k) {
    int[] result = new int[nums.length - k + 1];  // 存放结果
    
    // 对每个窗口进行处理
    for (int i = 0; i <= nums.length - k; i++) {
        int max = nums[i];  // 假设窗口第一个数是最大的
        
        // 看看窗口里其他数是不是更大
        for (int j = 1; j < k; j++) {
            if (nums[i + j] > max) {
                max = nums[i + j];
            }
        }
        
        result[i] = max;  // 记录这个窗口的最大值
    }
    
    return result;
}

这个方法很直观,就像用眼睛一个一个数字比较。但是,如果数组很长,窗口很大,这样做就会很慢。

聪明的解决方案:排队游戏

现在我们来学一个更聪明的方法。想象一个游戏:

  1. 我们有一群小朋友排队,每个小朋友手里举着一个数字牌。
  2. 我们要保证队伍里的小朋友,从前到后手里的数字是从大到小的。
  3. 当新的小朋友要进队时,就要把队伍后面所有比他数字小的小朋友请出队。
  4. 队伍最前面的小朋友,就是当前窗口的最大值。

用代码来实现这个想法:

public int[] maxSlidingWindow(int[] nums, int k) {
    // 如果数组是空的,直接返回空数组
    if (nums == null || nums.length == 0) return new int[0];
    
    int[] result = new int[nums.length - k + 1];  // 存放结果
    Deque<Integer> queue = new LinkedList<>();     // 这就是我们的"队伍"
    
    for (int i = 0; i < nums.length; i++) {
        // 第一步:如果队伍最前面的小朋友已经不在窗口范围内了,请他离开
        if (!queue.isEmpty() && queue.peek() < i - k + 1) {
            queue.poll();
        }
        
        // 第二步:新小朋友要入队,
        // 把队尾所有比新小朋友数字小的都请出队
        while (!queue.isEmpty() && nums[queue.peekLast()] < nums[i]) {
            queue.pollLast();
        }
        
        // 第三步:新小朋友入队
        queue.offer(i);
        
        // 第四步:如果窗口已经形成,记录当前最大值
        if (i >= k - 1) {
            result[i - k + 1] = nums[queue.peek()];
        }
    }
    
    return result;
}

让我们用一个具体的例子来看这个过程:

数组:[3,1,4,2]  窗口大小:2

初始状态:队伍为空

1. 数字3来了:
   队伍:[3]
   
2. 数字1来了:
   因为13小,直接排在3后面
   队伍:[3,1]
   第一个窗口的最大值是3
   
3. 数字4来了:
   41大,1离开队伍
   43大,3离开队伍
   队伍:[4]
   第二个窗口的最大值是4
   
4. 数字2来了:
   24小,直接排在4后面
   队伍:[4,2]
   第三个窗口的最大值是4

为什么这样做更好?

  1. 每个数字最多只会进队一次,出队一次
  2. 我们不用每次都看窗口里的所有数字
  3. 队伍的最前面永远是当前窗口的最大值

这就像在拍马拉松照片时,不用每次都量所有人的身高,而是保持一个有序的记录,随时知道当前画面中最高的人是谁。

小结

解决滑动窗口最大值问题,关键是要想到:

  1. 我们不需要记住窗口里的所有数
  2. 只需要保持一个"从大到小"的顺序
  3. 及时把不在窗口范围内的数字删除

这样,我们就把一个看起来很复杂的问题,变成了一个简单的"排队游戏"!


作者:忍者算法
公众号:忍者算法
c2f29413-f36f-4fc1-a443-9eabbdb400bc

posted @   忍者算法  阅读(20)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签
点击右上角即可分享
微信分享提示