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

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) 高效求解
相关推荐
北顾笙9802 小时前
day11-数据结构力扣
数据结构·算法·leetcode
月落归舟2 小时前
Lambda + Arrays---小练习
数据结构·算法
2601_955354462 小时前
seo臻系统和百度seo有什么区别
算法
君义_noip2 小时前
信息学奥赛一本通 1487:【例 2】北极通讯网络
算法·图论·信息学奥赛·csp-s
酉鬼女又兒3 小时前
零基础快速入门前端蓝桥杯 Web 备考:AJAX 与 XMLHttpRequest 核心知识点及实战(可用于备赛蓝桥杯Web应用开发)
前端·ajax·职场和发展·蓝桥杯·css3·js
June bug3 小时前
【AI赋能测试】基于 langchain+DeepSeek 的 AI 智能体
经验分享·功能测试·测试工具·职场和发展·langchain·自动化·学习方法
会编程的土豆3 小时前
【leetcode hot 100】二叉树二叉树
数据结构·算法·leetcode
XiYang-DING3 小时前
【LeetCode】203. 移除链表元素(Remove Linked List Elements)
算法·leetcode·链表
墨神谕3 小时前
希尔排序详解
数据结构·算法·排序算法