力扣滑动窗口两大压轴题:最小覆盖子串 + 滑动窗口最大值(保姆级思路 + 代码详解)

76. 最小覆盖子串

题目链接

76. 最小覆盖子串 - 力扣(LeetCode)

题目描述

给你字符串 s 和字符串 t,请你找出 s 中包含 t 所有字符的最短连续子串,如果没有返回空串。

核心思路

这道题是滑动窗口的经典难题,核心思想:

  1. 用两个哈希表:
    • need:记录t中每个字符需要的数量
    • window:记录当前窗口内字符的数量
  2. 右指针right不断向右扩大窗口
  3. 当窗口完全包含 t 所有字符 时,尝试移动左指针left缩小窗口,寻找最小长度
  4. 全程记录最小窗口的起始位置和长度,最后截取结果

代码实现(逐行注释)

复制代码
class Solution {
    public String minWindow(String s, String t) {
        // 边界判断:空字符串直接返回空
        if (s == null || s.isEmpty() || t == null || t.isEmpty()){
            return "";
        }

        // 记录 t 中字符的需求量
        Map<Character,Integer> need = new HashMap<>();
        // 记录当前窗口内字符的数量
        Map<Character,Integer> window = new HashMap<>();

        int left = 0, right = 0;    // 滑动窗口左右指针
        int strnumber = 0;          // 已匹配成功的字符种类数
        int minlen = Integer.MAX_VALUE;  // 最短子串长度
        int start = 0;              // 最短子串起始位置

        // 初始化 need 哈希表
        for(char c : t.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }

        // 右指针移动,扩展窗口
        while(right < s.length()){
            char r = s.charAt(right);
            right++;

            // 如果当前字符在 t 中,更新窗口计数
            if(need.containsKey(r)){
                window.put(r, window.getOrDefault(r, 0) + 1);
                // 该字符数量匹配成功
                if (need.get(r).equals(window.get(r))) {
                    strnumber++;
                }
            }

            // 窗口已完全包含 t 所有字符,开始收缩左边界
            while (strnumber == need.size()){
                // 更新最小窗口
                if (right - left < minlen){
                    start = left;
                    minlen = right - left;
                }

                // 左指针右移,尝试缩小窗口
                char l = s.charAt(left);
                if (window.containsKey(l)){
                    window.put(l, window.get(l) - 1);
                    // 该字符不再满足要求,匹配数减少
                    if (window.get(l) < need.get(l)) {
                        strnumber--;
                    }
                }
                left++;
            }
        }

        // 没有符合条件的子串返回空,否则截取结果
        return minlen == Integer.MAX_VALUE ? "" : s.substring(start, start + minlen);
    }
}

239. 滑动窗口最大值

题目链接

239. 滑动窗口最大值 - 力扣(LeetCode)

题目描述

给你一个数组 nums,有一个大小为 k 的窗口从左端滑到右端,你只能看到窗口内的 k 个数字,返回每个窗口的最大值。

核心思路

这道题不能暴力遍历,否则超时。正确解法:自定义单调队列 + 滑动窗口

核心规则:

  1. 维护一个单调递减队列,队首永远是当前窗口最大值
  2. 加入元素时:比当前元素小的全部弹出,保持队列递减
  3. 弹出元素时:只有要移除的元素 == 队首,才真正弹出
  4. 队首就是当前窗口最大值

代码实现(自定义单调队列 + 逐行注释)

复制代码
// 自定义单调递减队列
class Mydeque{
    Deque<Integer> deque = new LinkedList<>();

    // 弹出元素:判断是否为队首(最大值)
    void poll(int val){
        if(!deque.isEmpty() && val == deque.peek()){
            deque.poll();
        }
    }

    // 添加元素:比当前值小的全部移除,保持单调递减
    void add(int val){
        while(!deque.isEmpty() && val > deque.getLast()){
            deque.removeLast();
        }
        deque.add(val);
    }

    // 获取队首(当前窗口最大值)
    int peek(){
        return deque.peek();
    }
}

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        int len = n - k + 1;
        int[] res = new int[len];  // 存储每个窗口最大值
        int index = 0;

        Mydeque mydeque = new Mydeque();

        // 先把第一个窗口的元素加入队列
        for(int i = 0; i < k; i++){
            mydeque.add(nums[i]);
        }
        res[index++] = mydeque.peek();

        // 开始滑动窗口
        for(int i = k; i < n; i++){
            // 移除窗口左边移出的元素
            mydeque.poll(nums[i - k]);
            // 添加窗口右边新进入的元素
            mydeque.add(nums[i]);
            // 记录当前窗口最大值
            res[index++] = mydeque.peek();
        }

        return res;
    }
}

博客总结

这两道题是滑动窗口最常考的难题,也是面试高频手撕题:

  1. 最小覆盖子串:双哈希表 + 滑动窗口,找最短包含子串
  2. 滑动窗口最大值:自定义单调队列 + 滑动窗口,O (n) 高效求解
相关推荐
vibecoding日记6 小时前
双非如何快速入职字节等大厂大模型?真实案例分析:推理优化和投机解码
算法·求职·大模型工程师
yszaygr21389 小时前
Verilog参数化游程编码RLE模块
算法
望易9 小时前
刚设计的大模型架构-双域耦合认知框架
算法·架构
复杂网络13 小时前
多个 Claude Code 与多个 Codex 协同工作:设计与实现方案
算法
HjhIron1 天前
面试常客:字符串算法从入门到进阶
算法·面试
吴佳浩1 天前
DeepSeek DSpark:Confidence-Scheduled Speculative Decoding 技术解析
人工智能·算法·deepseek
触底反弹1 天前
🧠 搞懂 Token,才算真正入门大模型——从分词原理到 Embedding 语义实战
javascript·人工智能·算法
vivo互联网技术1 天前
ICLR 2026 | 基于后验采样的图像恢复方法LearnIR:人脸去阴影、去雾
人工智能·算法·aigc
浮生望2 天前
JS字符串与回文算法:从包装类到双指针的面试进阶之路
javascript·算法