Skip to content

滑动窗口与双指针(一)

About 4733 wordsAbout 16 min

滑动窗口

2024-03-31

labuladong算法笔记|滑动窗口算法

灵神|【题单】滑动窗口与双指针(定长/不定长/至多/至少/恰好/单序列/双序列/三指针)

今天重新整理滑动窗口的题目,开始参考灵神分享的题单并结合推荐的插件来做题。

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)

基础

1456. 定长子串中元音的最大数目 (20241218)

太强了,一看题解就懂的那种。

image-20241218152302452

这个题目涉及的是定长的窗口,先往里面填充数据,在更新答案,然后在将数据剔除。

这里我看下为什么i-k+1表示的是窗口的第一个数据的下标?

因为使用了一个位置来进行数据交换,中间是有两个数据是确定的,就是 k-1个数据是确定的,那么窗口的第一数据的下标就是 i - (k-1) ,转化就是 i - k + 1就表示窗口里面第一个数据的下标。

643. 子数组最大平均数 I(20241218)

使用模版求解。

1343. 大小为 K 且平均值大于等于阈值的子数组数目(20241218)

使用模版求解。

2090. 半径为 k 的子数组平均值(20241219)

这个题目还是有些是值得注意的。

题目给出的窗口是以i为中心,k为半径的窗口, 里面有一些细节,像是窗口的长度len 等于2 * k + 1, 循环的时候还是循环[0, nums.length),

对应的存放数据的下标是i-k,最前面需要移除元素的下标是i-len+1,相当于i - 2*k

Arrays.fill(ans, -1);将所有的元素都赋值成为-1.

2379. 得到 K 个黑块的最少涂色次数(20241219)

标准的模版解法,只是其中转化了一下思路,求窗口里面最多的黑色块x,最后用窗口的长度减去最多的黑色块x得到答案。

在移除时候的下标是 i - (k-1) ,也就是 i-k=1

1461. 检查一个字符串是否包含所有长度为 K 的二进制子串(20241219)

子串:连续的字符组成的字符串,一个长度为n的字符串所拥有的子串个数为: n * (n-1)/ 2 + 1,(子串是连续的)

子序列:在原来的序列中找出一部分元素组成的序列(子序列是不连续的)

长度为k的只包含字符'0'和字符'1'的子串个数等于2^k

开始想的是把所有长度为k的为包含0和1字符的子串都求出来,然后在求出来s的所有子串进行对比。

但是可以对比这两个子串的个数来求解这一道题,肯定是 2^k >= sub(s), 当两者想等的时候条件成立,如果2^k > sub(s)那么必定有一个或者多个子串不在s的子串里面,返回false。

2841. 几乎唯一子数组的最大和(20241219)

读题就读了半天, 简单来理解有两个条件, 一个是窗口里面的值求和为最大值,二个是窗口里面互不相同的元素的个数要大于等于m。

开始使用Set来做的,但是有一个用例nums = [1,1,1,3], k = 2, m=2 这个过不了,因为到[1, 3] 的时候,前面的1给从集合里面剔除掉了,导致出现了错误。

后面换成Map来做,统计每个元素的个数。

2461. 长度为 K 子数组中的最大和(20241219)

子数组 是数组中一段连续非空的元素序列。

子序列 是数组中非连续非空元素的序列。

这个题目和上面一个题目类似,都是在统计窗口元素和的同时,维护窗口中元素出现的次数问。

1423. 可获得的最大点数(20241219)

这个题很容易就会想到用双指针的思路来解,但是有会面临一个问题,就是左边的值和右边的值一样的情况下要怎么处理,所以换个思路思考:

拿走k张牌之后,剩下n-k张牌,这里的n是cardPoints的长度,那么剩下来的牌必然是连续的,想要求拿走的牌的最大点数,其实可以逆向思维思考,求剩下的连续的最小和,所以题目就变成了: 求窗口长度为n-k的最小和,然后用总和见到最小和就是题目所求的。

1652. 拆炸弹(20241220)

我悟到了

仔细阅读题目之后,我们都明白了,当k=0时全为1;当k>0的时候,值等于当前下标后面k个元素的和;当k<0时,值等于当前下标前面k个元素的和。

可以看出来,定长窗口都是向右移动的,重要的是确定第一个窗口的位置在哪里, 窗口的长度都是k。

当k>0的时候,我们很容易得到第一个窗口是 [1, k+1)(左端点为参照),当k<0时,我们也能够得到窗口的下标 [n - |k|, n)(右端点为参照)。

还需要解决的一个问题就是添加元素的下标剔除元素的下标

我们统一第一个窗口右侧下标为r, 那么窗口左侧下标就是r-|k|, 然后求余数分别就是[r%n, (r-|k|) % n]

1297. 子串的最大出现次数(20241220)

满足两个条件:1 子串不同字符个数小于maxLetters;2 子串长度大于minSize,小于maxSize

要出现次数最多,那么子串的长度就尽量短,所以可以转化成固定窗口长度为minSize的滑动窗口题目

结合灵神定长滑动窗口的思路解题。

补充:求map最大的value -> map.values().stream().max(Integer::compareTo).orElse(0);

2653. 滑动子数组的美丽值(20241220)

计数排序: ...

二、不定长滑动窗口

不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,以及求子数组的个数。

2.1、求最长或最大

3. 无重复字符的最长子串(20241223)

主要的思路就是把右侧的数据添加进来,然后判断是否已经存在了,如果已经存在了,那就缩小左边的窗口,直到没有重复的元素;如果不存在,就将元素添加到窗口里面。

3090. 每个字符最多出现两次的最长子字符串(20241223)

字符不重复解题思路相同, 只需要改变判断次数的代码。

有一个可以优化的思路,就是之前是用的Map<Character, Integer> has = new HashMap<>();来判断出现次数的;可以直接用数组来判断int[] has = new int[26];因为只有26个小写的英文字符。

1493. 删掉一个元素以后全为 1 的最长子数组(20241223)

因为求删除一个元素后全为1的最长子数组,求包含一个0,其他全为1的最长子数组。

当nums全为1的时候也同样满足。

1208. 尽可能使字符串相等(20241224)

对于这个题目,我甚至连题目都没有看懂,但是在看到下面题解关于题目的讲解的时候,我就明白了。

image-20241224174001217

在看到costs数组等于[1,1,1,2]的时候,在结合maxCost = 3的时候,解题思路就明朗了,就是先将两个字符穿转化的开销维护起来, 在使用灵神不定长滑动窗口的解题模板,很快就能解决了。

看来理解题目对于解题是相当重要的。

2730. 找到最长的半重复子字符串(20241224)

这个题题目理解起来还是比较容易的,就是求最多只有一对相邻字符的最大的子串。

定义same表示相同字符的对数,移动right, 当same>1的时候,就移动left,直到left和left-1对应的值相同,然后把same置为1。

这里容易陷入一个误区,就是可能会统计每个字符出现的次数,因为相同字符的对数不一样的话,处理的逻辑也不一样。

904. 水果成篮(20241225)

喵的题解都看不懂的题

用哈希表来处理要好容易理解一点。

还需要理解一个就是, ++ii++的区别。

1695. 删除子数组的最大得分(20241226)

题目的意思就是:求最大元素只出现一次的子数组的和。不定长滑动窗口求解。

2958. 最多 K 个重复元素的最长子数组(20241226)

和上面一题是相同的解决思路,相同的代码这里就不贴了。

刚才考虑到一个问题,就是用map能解决,用int[]数组能解决吗?

用数组可能不行,因为你不知道创建的数组的长度是多少, 给出的1 <= nums[i] <= 10^9, 1 <= nums.length <= 10^5 ,如果创建一个10^5大的数组,那么空间复杂度就非常的高。

2024. 考试的最大困扰度(20241227)

说实话,这个题我是看懂了的,但是就是写不出来。灵神的位运算现在的我还看不懂,后面学到的时候在回过头来看。

题意:求answerKey的一个最长子串,至多包含k个T或者至多包含k个F

滑动窗口

1004. 最大连续1的个数 III(20241227)

这个和上面一样的解题思路,确定不满足题意的条件,就是当子数组里面0出现的次数大于k的时候,就需要移动左端窗口了。

解题代码类似:

1658. 将 x 减到 0 的最小操作数(20241227)

逆向思维解这道题,x恰好减到0的最小操作数,可以想成求和为s-x的最长子数组,s表示数组的和。

Changelog

Last Updated: View All Changelog
  • feat(wiki): algo: 算法总结

    On 3/30/25

求求了,快滚去学习!!!

求求了求求了,快去学习吧!

【题单】贪心算法

不知道方向的时候,可以多看看书,书会给你指明下一步该干什么,加油!