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

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) 高效求解
相关推荐
ZhengEnCi18 分钟前
同样的模型,为什么本地和云 API 差别那么大:量化精度、聊天模板与采样参数的三大元凶
人工智能·算法·llm
Flynn Liang38 分钟前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai
liminla!1 小时前
SQL 从入门到进阶:语法速查与实战练习(完整优化版)
数据库·sql·算法
维克兜率天2 小时前
【维克】指数调整:哪些股票会被“强制买入“?
人工智能·python·算法
罗西的思考3 小时前
【轻量化复刻大模型】线性注意力交互式演示
人工智能·算法·机器学习
神也畏惧菠萝3 小时前
Day08 Python流程控制从入门到上瘾:if/match/while/for四件套,从B站登录一路打到猜数字游戏
python·算法·数据存储·for·while·if·ifelse
揽星河..3 小时前
【C语言】分支和循环
c语言·c++·算法
Fuyo_11195 小时前
C++ AVL树保姆级详解:从平衡因子到四大旋转机制
java·c++·算法
jktantan5 小时前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript