跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

滑动窗口算法详解与经典例题实战

滑动窗口算法利用双指针维护动态区间,单次遍历解决多类问题。涵盖最小和子数组、无重复最长子串、含 K 个零的最长连续 1 以及将 X 减至 0 的最小操作数四个经典场景。重点讲解单调性应用、哈希表去重、状态转换技巧及边界处理,提供 C++ 完整实现与逻辑图解,帮助读者掌握该算法的核心思想与实战变体。

猫巷少女发布于 2026/3/28更新于 2026/7/2936 浏览
滑动窗口算法详解与经典例题实战

滑动窗口核心思想

滑动窗口算法的核心在于用两个指针维护一个动态的区间。通过移动左右指针来扩大或缩小窗口,通常在一次遍历中就能完成计算,时间复杂度往往能优化到 O(n)。

典型应用场景:寻找最长无重复字符子串、找到和为目标值的最短子数组、字符串排列匹配等。

通用模板思路:

  1. 定义 left 和 right 指针,初始都指向起始位置;
  2. right 向右移动进窗口,更新状态;
  3. 当窗口不满足条件时,left 向右移动出窗口,直到重新满足;
  4. 在合法状态下更新结果。

结束条件通常是 right 指针越界。理解了这个框架,我们来看几个具体的例子。

案例一:长度最小的子数组

问题描述

给定一个含有 n 个正整数的数组和一个正整数 target,找出该数组中满足其和 ≥ target 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

解题思路

由于数组元素均为正整数,累加和具有单调性,这非常适合套用滑动窗口模板。

  1. 初始化 left 和 right 指针,同时指向首元素;
  2. right 指针向右移动,将元素加入窗口并累加 sum;
  3. 当 sum >= target 时,说明当前窗口满足条件,尝试收缩左边界以寻找更短的子数组:left 右移,sum 减去 nums[left],直到 sum < target;
  4. 每次收缩前都要记录当前窗口的最小长度。

注意:在收缩窗口(left 移动)的过程中,只要依然满足 sum >= target,就要持续更新结果,因为可能存在更优解。

代码实现

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0, right = 0;
        int len = INT_MAX, sum = 0;
        while(right < nums.size()) {
            sum += nums[right]; // 进窗口,累加
            while(sum >= target) { // 保证窗口合法
                len = min(len, right - left + 1); // 更新结果
                sum -= nums[left++]; // 出窗口
            }
            right++; // 继续进窗口
        }
        return len == INT_MAX ? 0 : len;
    }
};

案例二:无重复字符的最长子串

问题描述

给定一个字符串 s,请你找出其中不含有重复字符的最长子串的长度。

解题思路

我们需要判断字符是否重复。ASCII 字符集共 127 个,可以用一个大小为 128 的数组模拟哈希表。当一个字符进入窗口,对应位置计数加一;若计数大于 1,说明出现了重复。

  1. 定义 left 和 right 指针,同时指向字符串开头;
  2. right 向右移动,哈希表对应位置元素 ++;
  3. 一旦发现重复(hash[s[right]] > 1),left 开始右移,直到移除重复字符为止。移出过程中,对应哈希值要 --;
  4. 窗口合法时更新最大长度。

细节:left 向右移动(出窗口)时,必须同步减少哈希表中对应字符的计数,因为这些字符已经不在当前窗口中了。

代码实现

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[128] = {0};
        int left = 0, right = 0, size = s.size(), len = 0;
        while(right < size) {
            hash[s[right]]++; // 标记字符
            if(hash[s[right]] > 1) { // 发现重复
                while(s[left] != s[right]) { // 找重复字符,保证窗口合法
                    hash[s[left]]--; // 去掉标记
                    left++;
                }
                hash[s[left]]--; // 重复字符出窗口
                left++;
            }
            len = max(len, right - left + 1); // 更新结果
            right++; // 继续进窗口
        }
        return len;
    }
};

案例三:最大连续 1 的个数 III

问题描述

给定一个二进制数组 nums 和一个整数 k,允许翻转最多 k 个 0,求翻转后数组中连续 1 的最大个数。

解题思路

这道题本质上是允许窗口中包含最多 k 个 0。相比上一个案例,这里不需要去重,而是控制特定元素的数量。

  1. 定义 left 和 right 指针,cnt 记录窗口中 0 的个数;
  2. right 向右移动,如果是 0 则 cnt++;
  3. 如果 cnt <= k,窗口合法,直接更新结果;
  4. 如果 cnt > k,说明 0 太多了,需要 left 右移,直到 cnt 降回 k。

优化技巧:其实最长子串出现在窗口中 0 的个数等于 k+1 时,所以只需在 cnt > k 时更新结果即可。但为了防止遍历完整个数组 cnt 仍小于等于 k 的情况,最后还需要再更新一次结果。

代码实现

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int left = 0, right = 0;
        int size = nums.size();
        int result = 0, cnt = 0; // 记录结果和当前遍历到的 0 的个数
        while(right < size) {
            if(nums[right] == 0) {
                cnt++; // 0 个数更新
                if(cnt > k) { // 0 个数不满足要求
                    result = max(result, right - left); // 更新结果
                    while(cnt > k) {
                        if(nums[left] == 0) {
                            cnt--; // 0 个数--
                        }
                        left++;
                    }
                }
            }
            right++;
        }
        result = max(result, right - left); // 再次更新结果
        return result;
    }
};

案例四:将 x 减到 0 的最小操作数

问题描述

给你一个整数数组 nums 和一个整数 x。每一次操作时,你应当移除数组最左边或最右边的元素,并从 x 中减去该元素的值。如果可以将 x 恰好减到 0,返回最小操作数;否则返回 -1。

解题思路

直接思考从两边取数比较麻烦,不如换个角度:如果我们从两端取走了若干元素,它们的和为 x,那么中间剩下的部分和就是 sum - x。为了让操作数最少,也就是让取走的元素最少,等价于让中间剩下的子数组最长。

因此,问题转化为:在数组中找到一个最长的连续子数组,使其和等于 sum - x。因为所有元素都是正数,求和具有单调性,可以直接用滑动窗口解决。

  1. 预处理:计算数组总和 sum,目标值 val = sum - x;
  2. 若 val < 0,直接返回 -1;
  3. 使用滑动窗口寻找和为 val 的最长子数组,记录长度 len;
  4. 最终结果为 nums.size() - len。

细节处理:len 初始化为 -1,如果没找到满足条件的子数组,len 保持 -1,此时应返回 -1。

代码实现

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int sum = 0;
        for(auto e : nums) sum += e;
        
        int left = 0, right = 0;
        int val = sum - x;
        
        if(val < 0) return -1;
        
        int add = 0, len = -1;
        while(right < nums.size()) {
            add += nums[right++]; // 进窗口
            while(add > val) {
                add -= nums[left++]; // 出窗口
            }
            if(add == val) {
                len = max(len, right - left); // 更新结果
            }
        }
        
        if(len == -1) return -1;
        else return nums.size() - len;
    }
};

以上四个题目涵盖了滑动窗口在不同场景下的变体。掌握这些核心逻辑,遇到类似的双指针问题就能快速找到切入点。

目录

  1. 滑动窗口核心思想
  2. 案例一:长度最小的子数组
  3. 问题描述
  4. 解题思路
  5. 代码实现
  6. 案例二:无重复字符的最长子串
  7. 问题描述
  8. 解题思路
  9. 代码实现
  10. 案例三:最大连续 1 的个数 III
  11. 问题描述
  12. 解题思路
  13. 代码实现
  14. 案例四:将 x 减到 0 的最小操作数
  15. 问题描述
  16. 解题思路
  17. 代码实现
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于web的社区疫苗接种提醒和监控系统设 开题报告
  • CAN 报文信号矩阵与 DBC 解析异常排查
  • 2026年全球AI大模型深度研究报告
  • C++ explicit 关键字详解:作用、原因与使用
  • OpenClaw 钉钉对接教程:在 Linux 部署本地 AI 智能体
  • OpenClaw Docker 部署教程:集成飞书钉钉 QQ 机器人
  • 渗透测试常见面试题与核心知识点解析
  • 无人机多源融合定位:GPS/北斗标定、抗干扰与精度提升
  • Ubuntu 24.04 在线安装 Redis 8.x 教程
  • Python 数学可视化:显函数、隐函数及复杂曲线交互绘图
  • OpenClaw 安装与飞书机器人全流程配置指南
  • 数据处理:大模型训练的关键环节与实践
  • QClaw 本地化 AI 个人助手平台完全指南
  • Stable Diffusion 3 发布:20 亿参数 Medium 模型与 MMDiT 架构解析
  • Python Playwright 库详解:从入门到实战
  • 2026 年 1 月主流远程桌面工具横向评测与选型建议
  • Neo4j 图数据库使用入门
  • Go2 机器人强化学习(RL)开发实操指南
  • ORB-SLAM3 开源视觉与视觉惯性 SLAM 库详解
  • Docker Desktop 启动提示未检测到虚拟化支持的修复方案

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online