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

算法日记---滑动窗口

目录

前言

一、滑动窗口基本思路

1.1 什么是窗口

1.2 滑动窗口两种基本类型

1.3算法框架

二、固定大小窗口题目解析

1.1456. 定长子串中元音的最大数目

2.643.子数组最大平均数

三、可变大小窗口

1.3. 无重复字符的最长子串(hot100)

2.438. 找到字符串中所有字母异位词(hot100)

四、

核心思想

总结


前言

滑动窗口算法是处理数组和字符串子问题的高效方法,通过维护一个动态调整的区间(窗口),将时间复杂度从 O (n²) 优化到 O (n)。

本文将用 Java 实现各类滑动窗口问题


一、滑动窗口基本思路

1.1 什么是窗口

我们用生活中的例子进行类比:当你在超市排队结账时,队伍作为一个数组,你所观察的“前三个人”就是一个窗口

  • 窗口大小:3个人(固定大小)
  • 窗口滑动:当第一个人结账离开,你观察的窗口就像右滑动1位,变成了"第2~4个人"。

1.2 滑动窗口两种基本类型

根据窗口大小是否固定,分为两类:

类型特点应用
固定大小窗口窗口长度始终不变,仅通过右移更新窗口内容求数组中长度为 k 的子数组的最大和,求字符串中长度为 k 的子串的最大元音数
可变大小窗口窗口长度可动态扩大或缩小,通过 “右指针扩大窗口、左指针缩小窗口” 调整求最长无重复子串、求满足和≥target 的最短子数组

1.3算法框架

无论是上述那种类型,滑动窗口都需要依赖两个核心指针(左指针left,右指针right)来维护窗口

  1. 初始化:定义left=0(窗口左边界),right=0(窗口右边界),并初始化结果变量(如最大和、最长长度)和窗口状态变量(如当前窗口和、当前窗口内元素集合);
  2. 扩展窗口:移动right指针,将新元素加入窗口,更新窗口状态;
  3. 调整窗口:根据问题约束(如窗口大小固定、窗口内元素重复),判断是否需要移动left指针缩小窗口,更新窗口状态;
  4. 更新结果:在每次窗口调整后,判断是否满足结果条件,更新最终结果;
  5. 终止条件right指针遍历完数组 / 字符串。

二、固定大小窗口题目解析

1.1456. 定长子串中元音的最大数目

给你字符串 s 和整数 k 。

请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。

英文中的 元音字母 为(aeiou)。

class Solution {public int maxVowels(String S, int k) {char[] s = S.toCharArray();int ans = 0; // 记录最大元音数int vowel = 0; // 当前窗口的元音数for (int i = 0; i < s.length; i++) { // i为右边界// 入:新字符进入窗口,更新元音计数if (s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u') {vowel++;}// 窗口未满(长度 < k)时,只进不出,跳过后续步骤if (i < k - 1) { continue;}// 2. 更新:窗口满了(长度=k),记录当前最大值ans = Math.max(ans, vowel);// 3. 出:左边界字符离开窗口,更新元音计数(为下一次滑动做准备)char out = s[i - k + 1]; // 左边界L = i - k + 1(因为窗口长度为k,L= i -k +1)if (out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u') {vowel--;}}return ans;}
}

2.643.子数组最大平均数

给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。

请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

任何误差小于 10-5 的答案都将被视为正确答案。

class Solution {public double findMaxAverage(int[] nums, int k) {double sum = 0;// 先计算第一个窗口的和for (int i = 0; i < k; i++) {sum += nums[i];}// 初始化最大平均值为第一个窗口的平均值double maxAvg = sum / k;// 滑动窗口处理后续子数组for (int i = k; i < nums.length; i++) {// 窗口右边界加入新元素,左边界移除旧元素sum = sum + nums[i] - nums[i - k];// 计算当前窗口平均值,更新最大平均值double currentAvg = sum / k;maxAvg = Math.max(maxAvg, currentAvg);}return maxAvg;}
}

三、可变大小窗口

1.3. 无重复字符的最长子串(hot100)

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

class Solution {public int lengthOfLongestSubstring(String s) {// 哈希表:存储字符与其最后出现位置的映射// key: 字符,value: 该字符在字符串中最后出现的索引Map<Character, Integer> charIndex = new HashMap<>();int maxLen = 0; // 记录最长无重复子串的长度,初始为0int left = 0; // 左指针,标记当前窗口起始位置for (int right = 0; right < s.length(); right++) {// 获取当前右指针指向的字符char c = s.charAt(right);// 若字符已存在且索引在当前窗口内,则移动左指针至重复字符的后一项索引if (charIndex.containsKey(c) && charIndex.get(c) >= left) {left = charIndex.get(c) + 1;}charIndex.put(c, right); // 更新字符最新索引maxLen = Math.max(maxLen, right - left + 1);//计算当前窗口长度(right - left + 1)}return maxLen;}
}

2.438. 找到字符串中所有字母异位词(hot100)

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

class Solution {//因为要找 长度固定(和 p 长度相同) 的子串,且需要对比字符频率,所以用 滑动窗口 + 字符频率数组 的思路public List<Integer> findAnagrams(String s, String p) {List<Integer> result=new ArrayList<>();int slen=s.length();int plen=p.length();//固定窗口大小// 边界条件:如果s的长度小于p,不可能存在符合条件的子串,直接返回空列表if(slen<plen){return result;}// 定义两个数组用于统计字符频率(仅适用于小写字母)// 索引0-25分别对应字母a-zint[] sCount = new int[26];int[] pCount = new int[26];//处理s中前p.length()个字符,初始化s和p的频率数组for (int i = 0; i < plen; i++) {//p.charAt(i) - 'a':将字符转换为 0 - 25 的整数(因为题目中是小写字母,a 对应 0,b 对应 1,……,z 对应 25 ),这样就把字符映射到了 pCount 数组的索引上。pCount[p.charAt(i) - 'a']++;sCount[s.charAt(i) - 'a']++;}//初始化窗口状态变量---初始窗口匹配检查(s的前pLen个字符)是否与p是字母异位词if(isEqual(sCount,pCount)){result.add(0);//返回子串起始索引}//滑动窗口遍历剩余字符for(int right=plen;right<slen;right++){//右指针右移,加入新字符sCount[s.charAt(right)-'a']++;// 左指针右移,移除旧字符(窗口左端移出的字符)sCount[s.charAt(right - plen) - 'a']--;// 检查当前窗口是否匹配if (isEqual(sCount, pCount)) {result.add(right - plen + 1);}}return result;}// 辅助方法:判断两个频率数组是否相等private boolean isEqual(int[] a, int[] b) {for (int i = 0; i < 26; i++) {if (a[i] != b[i]) return false;}return true;}
}

四、核心思想

  • 维护左右两个指针表示窗口的边界
  • 初始化结果变量/初始化窗口状态变量
  • 根据问题类型决定窗口是固定大小还是可变大小
  • 右指针通常用于扩大窗口,左指针用于缩小窗口
  • 每次调整窗口后更新结果


总结

以上即对滑动窗口算法的解析,希望自己能在接下来时间坚持把算法整起来了,不要拖拉了。


文章转载自:

http://sYRQCJUT.qzdxy.cn
http://ppJyer6O.qzdxy.cn
http://irWIGaeE.qzdxy.cn
http://Tnzv0XxB.qzdxy.cn
http://P6WNK9DP.qzdxy.cn
http://VtEVM0rK.qzdxy.cn
http://4jN7xSUU.qzdxy.cn
http://tAzL08VY.qzdxy.cn
http://5vVAPWtD.qzdxy.cn
http://4rU0e7r5.qzdxy.cn
http://LrzSLasW.qzdxy.cn
http://F65mphY7.qzdxy.cn
http://tLbjGyMd.qzdxy.cn
http://qafBjY4n.qzdxy.cn
http://p1WASjMR.qzdxy.cn
http://sSuNnDVV.qzdxy.cn
http://PbCTPrMH.qzdxy.cn
http://FxBUgfem.qzdxy.cn
http://k6cgmdMB.qzdxy.cn
http://ImIwafKt.qzdxy.cn
http://f8hzvIBl.qzdxy.cn
http://bqWqz52F.qzdxy.cn
http://zu3xpuS9.qzdxy.cn
http://kcwRUHZk.qzdxy.cn
http://7zDvKCor.qzdxy.cn
http://frlPLvEs.qzdxy.cn
http://nibbOcrh.qzdxy.cn
http://mjg0NobL.qzdxy.cn
http://6ToKBWPD.qzdxy.cn
http://bFlGsjh8.qzdxy.cn
http://www.dtcms.com/a/373429.html

相关文章:

  • 《嵌入式硬件(四):温度传感器DS1820》
  • 动态规划-学习笔记
  • Java分布式锁详解
  • Docker学习笔记(四):网络管理与容器操作
  • 基于MATLAB的FIR和IIR低通带通滤波器实现
  • SpringMVC 程序开发
  • 深入理解 Linux hostname 命令:从日常操作到运维实战
  • SN码追溯技术全景解析:AI时代的数字身份革命
  • AI 小白入门:探索模型上下文协议(MCP)及其前端应用
  • 代码随想录70期day5
  • Vue3源码reactivity响应式篇之reactive响应式对象的track与trigger
  • GitHub高星标项目:基于大数据的心理健康分析系统Hadoop+Spark完整实现
  • Google Guice @Inject、@Inject、@Singleton等注解的用法
  • 【MATLAB组合导航代码,平面】CKF(容积卡尔曼滤波)作为融合方法,状态量8维,观测量4维,包含二维平面上的严格的INS推导。附完整代码
  • Go Style 代码风格规范
  • Java 16 中引入的 record的基本用法
  • uni-app iOS 性能监控全流程 多工具协作的实战优化指南
  • shell 中 expect 详解
  • 告别低效:构建健壮R爬虫的工程思维
  • Ubuntu中显示英伟达显卡的工具软件或者指令
  • 银行卡号识别案例
  • 【golang学习笔记 gin 】1.2 redis 的使用
  • AI提示词(Prompt)基础核心知识点
  • VTK开发笔记(五):示例Cone2,熟悉观察者模式,在Qt窗口中详解复现对应的Demo
  • Excel 表格 - Excel 减少干扰、专注于内容的查看方式
  • Vue3 + Ant Design Vue 全局配置中文指南
  • CSS in JS 的演进:Styled Components, Emotion 等的深度对比与技术选型指引
  • 哈士奇vs网易高级数仓:数据仓库的灵魂是模型、数据质量还是计算速度?| 易错题
  • Windows 命令行:cd 命令2,切换到多级子目录
  • C++ 8