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

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) 高效求解
相关推荐
旖-旎16 小时前
深搜(二叉树剪枝)(3)
数据结构·c++·算法·力扣·剪枝·递归
流年如夢16 小时前
结构体:定义、使用与内存布局
c语言·开发语言·数据结构·c++·算法
『昊纸』℃17 小时前
C语言学习心得集合 篇1
c语言·算法·编程基础·学习心得·实践操作
Chase_______17 小时前
LeetCode 1456:定长子串中元音的最大数目
算法·leetcode
小O的算法实验室17 小时前
2026年IEEE IOTJ,DNA序列启发相似性驱动粒子群算法+无人机与基站部署,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
谭欣辰17 小时前
Floyd算法:动态规划解最短路径
c++·算法·图论
计算机安禾17 小时前
【Linux从入门到精通】第12篇:进程的前后台切换与信号控制
linux·运维·算法
6Hzlia17 小时前
【Hot 100 刷题计划】 LeetCode 84. 柱状图中最大的矩形 | C++ 两次单调栈基础扫法
c++·算法·leetcode
C雨后彩虹17 小时前
文件目录大小
java·数据结构·算法·华为·面试
0南城逆流017 小时前
【技术点】嵌入式技术考点三:数据结构
java·数据结构·算法