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

算法学习笔记:双指针_滑动窗口专题

目录

1.长度最小的子数组

2.无重复字符的最长子串

3.将x减少到0的最小操作数

4.最大连续1的个数Ⅲ

5.找到字符串中所有字母异位词

6.水果成篮

7.串联所有单词的子串

8.最小覆盖子串

1.长度最小的子数组:209. 长度最小的子数组 - 力扣(LeetCode)中等

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

3.将x减少到0的最小操作数:1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)中等

4.最大连续1的个数Ⅲ:1004. 最大连续1的个数 III - 力扣(LeetCode)中等

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

6.水果成篮:904. 水果成篮 - 力扣(LeetCode)中等

7.串联所有单词的子串:30. 串联所有单词的子串 - 力扣(LeetCode)困难

8.最小覆盖子串:76. 最小覆盖子串 - 力扣(LeetCode)困难

滑动窗口。1.特点:具有单调性。2.步骤:进窗口、判断窗口、出窗口。然后记录结果在三个步骤其一

1.长度最小的子数组

(1)链接:209. 长度最小的子数组 - 力扣(LeetCode)中等

题意:找到一个最短的子数组(连续),要求子数组的和>=目标值。

(2)思路:滑动窗口不断求和,当和>=目标值时,则判断结果并出窗口

可以使用滑动窗口的原因是:数组元素都是正整数,不断向右递增时数组和是递增的

(3)代码

class Solution {public int minSubArrayLen(int target, int[] nums) {int ret = Integer.MAX_VALUE, sum = 0;for(int left=0,right=0;right<nums.length;right++) {sum += nums[right]; //1.进窗口while(sum >= target) { //2.判断ret = Math.min(right-left+1,ret); //3.记录结果sum -= nums[left++]; //4.出窗口}}return ret==Integer.MAX_VALUE?0:ret;}
}
2.无重复字符的最长子串

(1)链接:3. 无重复字符的最长子串 - 力扣(LeetCode)中等

题意:找出没有重复字符的最长子数组,也就是求最长子数组的长度

(2)思路:也是找子数组,所以可以使用滑动窗口。

如何判断是否有重复元素,则可以遍历窗口内元素即可

(3)代码

class Solution {public int lengthOfLongestSubstring(String s) {//如何判断窗口内是否有重复元素??从窗口左边开始遍历,是否和新进入的窗口值相同//做法:始终保持窗口内的元素是不重复的int ret = 0;int n = s.length();for(int left=0,right=0;right<n;right++) {//1.入窗口--这里默认窗口为[left,right]的元素int cur = left;while(cur < right) { //2.判断-窗口内是否有重复元素if(s.charAt(cur) == s.charAt(right)) {left = cur+1; //3.出窗口}cur++;}ret = Math.max(ret,right-left+1); //4.记录结果-此时的窗口内保证无重复元素}return ret;}
}
3.将x减少到0的最小操作数

(1)链接:1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)中等

题意:每次从最左或者最右选取一个数(选取完默认从数组中删除),要求和为x且长度最小的。这是正向的意思,我们反过来理解:在中间选取一段连续区间和为sum-x,要求长度最长

(2)思路:连续数组且都是正整数,且和为sum-x

(3)代码

class Solution {public int minOperations(int[] nums, int x) {//题意:每次选择最左边或者最右边的数,是否可以组合成x。//题意转换:数组和为sum,是否存在这样一个区间,使得和为sum-x的,要长度最大int sum = 0, n = nums.length;for(int i=0;i<n;i++) {sum += nums[i];}int target = sum - x, len = -1, path = 0;for(int left=0,right=0;right<n;right++) {path += nums[right]; //1.入窗口while(left < n && path > target) { //2. 判断//3.出窗口path -= nums[left];left++;}//4.记录结果if(path == target) len = Math.max(len,right-left+1);}return len==-1?-1:n-len;}
}
4.最大连续1的个数Ⅲ

(1)链接:1004. 最大连续1的个数 III - 力扣(LeetCode)中等

题意:找出含有最多1的子数组,有k次机会可以将0变成1

(2)思路:连续子数组,可以使用滑动窗口。窗口内维护1的个数和将0变成1的次数

出窗口的条件:当0的个数大于K次时出窗口

(3)代码

class Solution {public int longestOnes(int[] nums, int k) {//窗口内维护1,和可将0变成1的个数int n = nums.length;int ret = 0, count=0;for(int left=0,right=0;right<n;right++) {if(nums[right] == 0) count++; //1.入窗口while(count > k) { //2.判断//3.出窗口if(nums[left] == 0) count--;left++;}//4.记录结果ret = Math.max(ret,right-left+1);}return ret;}
}
5.找到字符串中所有字母异位词

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

题意:给两个字符串,要求在s字符串中找出所有和p字符串互为异位词的字符串

(2)思路:(窗口不断向右移动,字母的数量是不断增加的)窗口只需要维护p长度的大小即可,每次到达该长度,就判断一下是否互为异位词,判断完记录结果并出窗口。

用两个计数数组判断是否互为异位词

(3)代码

class Solution {public List<Integer> findAnagrams(String s, String p) {//如何判断异位词??int[] pMap = new int[26];for(int i=0;i<p.length();i++) {pMap[p.charAt(i)-97]++;}int[] sMap = new int[26];List<Integer> ret = new ArrayList<>();for(int left=0,right=0;right<s.length();right++) {//1.入窗口sMap[s.charAt(right)-97]++;//2.判断if(right-left+1 == p.length()) {//3.判断是否为异位词boolean update = true;for(int i=0;i<26;i++) {if(sMap[i] != pMap[i]) {update = false;break;}}//4.记录结果if(update) ret.add(left);//5.出窗口sMap[s.charAt(left++)-97]--;}}return ret;}
}
6.水果成篮

(1)链接:904. 水果成篮 - 力扣(LeetCode)中等

题意:给一个数组,不同的值代表不同的水果。最多只能采摘两种水果,求可以采摘的最多数量是多少?

(2)思路:不断向右移动,数量增加,且是连续子数组。

每次入窗口判断对应的map是否为空,为空则为不同的种类。当种类数大于2说明超出需要出窗口;最后记录结果即可。

(3)代码

class Solution {public int totalFruit(int[] fruits) {//如何判断水果的种类和之前的相同或者不同??可以用map记录,如果=0则不同int[] map = new int[fruits.length];int ret = 0, kind = 0;for(int left = 0,right = 0; right < fruits.length; right++) {//1.入窗口并判断水果种类是否增加if(map[fruits[right]] == 0) {kind++;}map[fruits[right]]++;//2.判断-种类是否超出2种while(kind > 2) {//3.出窗口map[fruits[left++]]--;if(map[fruits[left-1]] == 0) kind--;}//4.记录结果ret = Math.max(ret,right-left+1);}return ret;}
}
7.串联所有单词的子串

(1)链接:30. 串联所有单词的子串 - 力扣(LeetCode)困难

题意:有一个字符串数组words,里面的字符串长度都相同,把它们以任何的顺序组成一个字符串(单个单词内部顺序不变)。然后求该字符串在字符串s中出现的起始位置

(2)思路:把整个单词看成一个字母,也就是找这些字母的异位词出现的起始位置

1)先用Map存储words中字符串出现的频率

2)滑动窗口每次移动len步,每次进入窗口的单词为right+len

3)进入窗口后,同时记录map中,并判断该单词是否为有效单词,如果有效则记录count

4)如果count达到有效次数,则记录结果

5)滑动窗口需要执行len次

(3)代码:

class Solution {public List<Integer> findSubstring(String s, String[] words) {int n = words.length, len = words[0].length();Map<String,Integer> mapW = new HashMap<>(); //记录words里面单词出现的频率for(String x : words) {mapW.put(x,mapW.getOrDefault(x,0)+1);}List<Integer> ret = new ArrayList<>();for(int k=0;k<len;k++) { //控制滑动窗口执行的次数Map<String,Integer> mapS = new HashMap<>(); //记录窗口内里面单词出现的频率for(int left=k,right=k,count=0;right+len <= s.length();right+=len) {//每次进窗口为right+len, 用count记录窗口内有效字符串的个数//1.进窗口String in = s.substring(right,right+len); //[right,right+len-1]mapS.put(in,mapS.getOrDefault(in,0)+1);//2.维护窗口内有效字符串个数if(mapS.get(in) <= mapW.getOrDefault(in,0)) count++; //如果in字符串在mapW也存在,且个数<=,说明是有效字符串//3.判断if(right-left+1 > len * n) {String out = s.substring(left,left+len);if(mapS.get(out) <= mapW.getOrDefault(out,0)) count--; //如果out字符串在mapW也存在,且个数<=,说明是有效字符串//4.出窗口mapS.put(out,mapS.get(out)-1);left += len;}//5.记录结果if(count == n) ret.add(left);}}return ret;}
}

(*)不使用该计数数组,使用map如果做?

1)借助一个变量count统一窗口中有效“字符”的个数。这里的字符可能是单个字母,也可能是一个字符串。

2)进窗口时,判断两个hash中的字符个数,如果进窗口的hash个数小于原hash个数

8.最小覆盖子串

(1)链接:76. 最小覆盖子串 - 力扣(LeetCode)困难

题意:在字符串s中找出一个连续的子串,要求这个子串包含字符串t。并且要返回这个最短的子串

(2)思路:使用两个哈希表或者计数数组,维护有效数字的窗口即可。

只需要记录最短的长度和起始位置。

(3)代码

使用Map接口:

class Solution {public String minWindow(String s, String t) {Map<Character,Integer> mapT = new HashMap<>();for(int i=0;i<t.length();i++) {char ch = t.charAt(i);mapT.put(ch,mapT.getOrDefault(ch,0)+1);}int minLen = Integer.MAX_VALUE, begin = -1;Map<Character,Integer> mapS = new HashMap<>();for(int left = 0,right = 0,count = 0;right < s.length();right++) {//1.入窗口char ch = s.charAt(right);mapS.put(ch,mapS.getOrDefault(ch,0)+1);//2.判断是否有效if(mapS.get(ch) <= mapT.getOrDefault(ch,0)) count++;//3.维护窗口while(left <= right && count >= t.length()) {//4.记录结果if(count == t.length() && minLen > right - left + 1) {begin = left;minLen = right - left + 1;}//5.出窗口char out = s.charAt(left);if(mapS.get(out) <= mapT.getOrDefault(out,0)) count--;mapS.put(out,mapS.get(out)-1);left++;}           }if(begin == -1) return new String();else return s.substring(begin,begin+minLen);}
}

使用数组模拟:

class Solution {public String minWindow(String ss, String tt) {char[] s = ss.toCharArray();char[] t = tt.toCharArray();int[] mapT = new int[128];//记录第一轮mapint kinds = 0;for(int i = 0;i < t.length;i++) {if(mapT[t[i]] == 0) kinds++;mapT[t[i]]++;}int[] mapS = new int[128];int minlen = Integer.MAX_VALUE, begin = -1;for(int left=0,right=0,count=0;right<s.length;right++) {//1.入窗口char in = s[right];mapS[in]++;//2.判断是否为有效种类if(mapS[in] == mapT[in]) count++;//3.循环while(count == kinds) {//4.记录窗口if(right-left+1 < minlen) {begin = left;minlen = right-left+1;}//5.出窗口char out = s[left++];if(mapS[out] == mapT[out]) count--;mapS[out]--;}}if(begin == -1) return new String();else return ss.substring(begin,begin+minlen);}   
}

http://www.dtcms.com/a/355833.html

相关文章:

  • STM32CubeMX 6.15.0 + CLion
  • 3.渗透-.IP地址-详解
  • AI批量抠图工具,无需付费,永久使用
  • 报错记录 Error: CUDNN_STATUS_BAD_PARAM; Reason: finalize_internal()
  • 仓颉编程语言青少年基础教程:输入输出
  • Vue 自定义筛选组件:3 种全选反选场景的优雅实现
  • Groovy闭包语法详解
  • 【MySQL数据库入门课程】开课啦!
  • 一个是“内容生成器”,一个是“知识管理器”:VibeVoice 与 NoteBookLM 优劣对决?
  • 《零基础入门AI:YOLO目标检测模型详解(从基础概念到YOLOv1核心原理)》
  • Vue模版:Ant-design模态框弹窗
  • vue3的setup
  • 基于离散小波变换(DWT)的数字水印Matlab
  • @Indexed注解的作用
  • 整理3点结构点与点之间的距离
  • Paimon——官网阅读:非主键表
  • 基于pytorch的垃圾分类识别项目实战
  • Qt|QElapsedTimer 的使用详解
  • H5小游戏-超级马里奥
  • 【涂鸦T5】2. 光感bh1750
  • 效率飙升200%:Appsmith开发结合cpolar远程访问实战解析
  • 前端面试题2(vue)
  • 高并发内存池(14)- PageCache回收内存
  • Go 语言常用命令使用与总结
  • 【Agent】AutoAgent: A Fully-Automated and Zero-Code Framework for LLM Agents
  • 从零开始:手写数字识别程序的深度学习实践
  • 《实际项目》空调水系统群控方案
  • TensorFlow 深度学习 | 三种创建模型的 API
  • Promptalot-Midjourney提示词分享平台
  • Java爬虫是什么,如何获取API接口