当前位置: 首页 > news >正文

滑动窗口算法详解:从理论到实战(LeetCode 3 438)

一、算法思想

滑动窗口是一种处理数组/字符串子区间问题的高效算法,通过维护一个动态变化的窗口区间,用双指针(左指针left、右指针right)在O(n)时间复杂度内解决问题。

核心特点:

  • 窗口动态调整:右指针探索新元素,左指针收缩无效区间
  • 状态实时更新:用哈希表等数据结构记录窗口内元素状态
  • 避免重复计算:通过指针移动而非重新遍历来更新结果

二、典型案例分析

案例1:无重复字符的最长子串(LeetCode 3)

3. 无重复字符的最长子串 - 力扣(LeetCode)

题目描述

找出字符串中不包含重复字符的最长子串长度

算法思路:

我们先从每一个题目给出的字符开始,找出不同的不重复的最长子串,我们可以发现,当我们增加子串的起始位置时,终点位置也是一样增加的,所以我们设置滑动窗口的时候,只需要遍历所有的左指针,然后我们

class Solution {
public:// 查找连续且不重复的子串   int lengthOfLongestSubstring(string s) {// 用哈希表记录每一个字符出现的次数unordered_set<char> occ;// 初始化右指针为-1,表示一开始这个指针在左指针的左边int right = -1, ans = 0;int n = s.size();// 开始遍历所有的左指针for(int i = 0; i < n; i++){if(i != 0){// 如果不是初始的状态的话,就要减去之前子串的第一个字符occ.erase(s[i - 1]);}while(right + 1 < n && !occ.count(s[right + 1])){// 如果说右边界+1没有越界,并且这个字符之前没有出现过的话,就可以添加occ.insert(s[right + 1]);right++;}ans = max(ans, right - i + 1);}return ans;}
};
关键点分析
  1. 哈希表记录位置:快速判断重复并定位
  2. 左指针跳跃:遇到重复时直接跳到重复字符的下一位
  3. ABBA情况处理max()保证指针不反向移动
复杂度
  • 时间复杂度:O(n)
  • 空间复杂度:O(字符集大小)

案例2:找到字符串中所有字母异位词(LeetCode 438)

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

题目描述

给定字符串s和p,找到s中所有p的字母异位词的子串起始索引

算法思路:

        首先,根据题目我们得知,s的字符串的长度必须要大于等于p的字符串的长度,不然的话,是不可能获得关于p的异位词的。

// 如果说s的长度比p的长度要小的话,证明字符串s中一定不存在字符串p的异位词。
if(sLen < pLen){return vector<int>();
}

        然后,我们需要获得每一个滑动窗口中,每一个字符的出现次数,滑动窗口的大小保持不变(根据题目要求可得),在算法的实现中,我们可以使用数组来存储字符串 p 和滑动窗口中每种字母的数量。

// 如果从0开始到pLen-1的子串符合题目要求,增加0索引到结果的集合中
if (sCount == pCount) {ans.emplace_back(0);
}
// 在s中构造一个长度为与字符串p的长度相同的滑动窗口,并在滑动中维护窗口中每种子符的数量
for (int i = 0; i < sLen - pLen; i++) {--sCount[s[i] - 'a'];       // 这个表示移除左边界字符++sCount[s[i + pLen] - 'a'];if (sCount == pCount) {ans.emplace_back(i + 1);        // 添加索引到结果中}
}
最终算法实现
class Solution {
public:vector<int> findAnagrams(string s, string p) {int sLen = s.size(), pLen = p.size();   // 获得s和p的字符串长度// 如果说s的长度比p的长度要小的话,证明字符串s中一定不存在字符串p的异位词。if(sLen < pLen){return vector<int>();}vector<int> ans;        // 保存有多少个结果vector<int> sCount(26); // s字符串有多少个字母出现vector<int> pCount(26); // p字符串有多少个字母出现for(int i = 0; i < pLen; i++){++sCount[s[i] - 'a'];       // 开始统计每一个数字的大小++pCount[p[i] - 'a'];}if(sCount == pCount){ans.emplace_back(0);}// 在s中构造一个长度为与字符串p的长度相同的滑动窗口,并在滑动中维护窗口中每种子符的数量for(int i = 0; i < sLen - pLen; i++){--sCount[s[i] - 'a'];       // 这个表示移除左边界字符++sCount[s[i + pLen] - 'a'];if(sCount == pCount){ans.emplace_back(i + 1);        // 添加索引到结果中}}return ans;}
};
关键点对比
特性案例1案例2
窗口类型可变长度固定长度
核心数据结构哈希表记录位置频率数组/哈希表
指针移动策略跳跃式移动滑动式移动
结果更新时机每次移动右指针时窗口达到大小时

三、滑动窗口通用模板

#include <unordered_map>
#include <string>using namespace std;int sliding_window_template(const string& s) {int left = 0;                     // 左指针初始化unordered_map<char, int> counter; // 滑动窗口计数器int result = 0;                   // 最终结果存储for (int right = 0; right < s.size(); ++right) {// 1. 将s[right]加入窗口(示例:字符计数)char c = s[right];counter[c]++;// 2. 判断收缩窗口的条件(根据具体问题实现)while (/* 窗口需要收缩的条件,例如:counter[c] > 1 */) {// 3. 可选:记录/更新中间结果// result = max(result, right - left);// 4. 移出左边界元素char left_char = s[left];if (--counter[left_char] == 0) {counter.erase(left_char); // 清除空计数}left++; // 收缩窗口}// 5. 更新最终结果(根据问题需求调整位置)result = max(result, right - left + 1);}return result;
}

四、算法应用场景

  1. 子串/子数组问题
  2. 需要统计频率/出现次数的场景
  3. 寻找连续区间的最优解
  4. 时间复杂度优化需求(暴力解为O(n²)时)

五、高频面试考点

  1. 如何确定窗口收缩条件?
  2. 哈希表与数组的选择策略
  3. 边界条件处理(如空字符串、全重复字符)
  4. 空间复杂度优化技巧

 写在最后:

我们可以在这里学习C++知识:

0voice · GitHub

http://www.xdnf.cn/news/534295.html

相关文章:

  • 自动化测试的框架有哪些?原理是什么?
  • 深入掌握MyBatis:连接池、动态SQL、多表查询与缓存
  • springboot+mybatis或mybatisplus在进行%name%的前后模糊查询时如何放防止sql注入
  • 汇川MD810-20M4110GXXX变频器为什么要加GRJ9000S电源滤波器?
  • C# 深入理解类(属性)
  • python打卡day30
  • Navicat连接开启sm3认证的瀚高数据库
  • 网络请求和状态管理
  • SAP学习笔记 - 开发13 - CAP 之 添加数据库支持(Sqlite)
  • 《虚实共生:双向映射重塑具身智能决策逻辑》
  • 5.19 打卡
  • 存储系统02——Libevent事件循环
  • Interrupt 2025 大会回顾:关于LangChain 的 AI Agent会议内容总结
  • anythingLLM支持本地大模型嵌入知识库后进行api调用
  • Linux 系统异常触发后自动重启配置指南
  • 深入解析PyTorch中MultiheadAttention的参数key_padding_mask与attn_mask
  • 【AI时代】Java程序员大模型应用开发详细教程(上)
  • ALTER AGGREGATE使用场景
  • Pod 节点数量
  • 【Game】Powerful——Punch and Kick(12)
  • 阿里世界偏好模型:WorldPM-72B论文速读
  • LangChain框架核心技术:从链式工作流到结构化输出的全栈指南
  • Spring的后置处理器是干什么用的?扩展点又是什么?
  • 数据结构学习笔记—初识数据结构
  • 用Caffeine和自定义注解+AOP优雅实现本地防抖接口限流
  • 玉米籽粒发育
  • spring boot 注解 @bean
  • 打卡30天
  • 【IDEA】删除/替换文件中所有包含某个字符串的行
  • ROS2简介