长度最小的子数组
滑动窗解法
算法题关键词:
满足xxx条件(计算结果,出现次数,同时包含)
最长/最短
字串/子数组/子序列
核心思想:
滑动窗使用思路(寻找最长)
——核心:左右双指针(R,L)在起始点,R向右逐位滑动循环
——每次滑动过程中
如果:窗内元素满足条件,R向右扩大窗口,并更新最优结果
如果:窗内元素不满足条件,L向右缩小窗口
——R到达结尾
滑动窗使用思路(寻找最短)
——核心:左右双指针(R,L)在起始点,R向右逐位滑动循环
——每次滑动过程中
如果:窗内元素满足条件,L向右缩小窗口,并更新最优结果
如果:窗内元素不满足条件,R向右扩大窗口
——R到达结尾
模板
最长模板:
初始化left,right,result,bestResult
while(右指针没有到达结尾)
{
窗口扩大,加入right对应的元素,更新当前result
while(result不满足要求)
{
窗口缩小,移除left对应元素,left右移
}
更新最优结果bestResult
right++
}
最短模板:
初始化left,right,result,bestResult
while(右指针没有到达结尾)
{
窗口扩大,加入right对应的元素,更新当前result
while(result满足要求)
{
更新最优结果bestResult
窗口缩小,移除left对应元素,left右移
}
right++
}
返回bestResult
暴力解法
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0)
{
return 0;
}
int ans = INT_MAX;
for (int i = 0; i < n; i++)
{
int sum = 0;
for (int j = i; j < n; j++)
{
sum += nums[j];
if (sum >= s)
{
ans = min(ans, j - i + 1);
break;
}
}
}
return ans == INT_MAX ? 0 : ans;
}
};
滑动窗口
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0)
{
return 0;
}
int ans = INT_MAX;
int start = 0, end = 0;
int sum = 0;
while (end < n)
{
sum += nums[end];
while (sum >= s)
{
ans = min(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == INT_MAX ? 0 : ans;
}
};
滑动窗口是一种常用的算法技巧,适用于多种类型的题目,以下是一些常见的适用题型:
1. 子数组/子串问题
- 无重复字符的最长子串:例如给定一个字符串,要求找出其中不含有重复字符的最长子串的长度。像在字符串“abcabcbb”中,无重复字符的最长子串是“abc”,长度为3。这类问题可以通过滑动窗口不断扩大和缩小,同时利用哈希表等数据结构记录窗口内字符的出现情况,来高效地找到结果。
- 有k个不同字符的子串:给定一个字符串和一个整数k,找出字符串中包含k个不同字符的最长子串 。通过滑动窗口的移动,动态维护窗口内不同字符的数量和子串长度,从而找到满足条件的子串。
- 最小覆盖子串:给定两个字符串s和t,在s中找到包含t中所有字符的最小子串。利用滑动窗口不断调整子串的起始和结束位置,直到找到满足条件的最小子串。
2. 求子数组的最大/最小和、长度问题
- 和至少为K的最短子数组:给定一个整数数组和一个整数K,找出该数组中和至少为K的最短连续子数组的长度。通过滑动窗口的方法,不断调整窗口的起始和结束位置,计算窗口内子数组的和,从而找到满足条件的最短子数组长度。
- 长度为K的最大子数组和:在给定的数组中,找到长度为固定值K的连续子数组,使其元素和最大。通过滑动窗口在数组上滑动,每次计算窗口内的子数组和并进行比较,找到最大和的子数组。
3. 判断是否存在满足条件的区间
- 判断数组中是否存在和为目标值的连续子数组:给定一个整数数组和一个目标值,判断是否存在连续的子数组,其和等于目标值。可以通过滑动窗口在数组上移动,计算窗口内子数组的和,判断是否等于目标值。
- 判断字符串中是否存在异位词:给定两个字符串s和p,找出s中所有p的异位词的起始索引。通过滑动窗口在字符串s上滑动,利用数组或哈希表记录窗口内字符的出现次数,与字符串p的字符出现次数进行比较,判断是否为异位词。
4. 滑动窗口在其他场景的应用
- 在数据流中找特定模式:比如在不断到来的整数数据流中,找到满足特定条件(如连续若干个数的和在某个范围内)的子序列。
- 处理环形数组问题:对于环形数组(即数组的首尾相连),可以通过将数组长度翻倍等技巧,利用滑动窗口来处理一些与连续子数组相关的问题。
以下是一些力扣上典型的滑动窗口题目:
1. 固定大小滑动窗口
- 题目1456. 定长子串中元音的最大数目
- 题目描述:给你字符串 s 和整数 k ,请你返回 s 中长度为 k 的子串包含元音的最大数目。元音字母是 'a' 、 'e' 、 'i' 、 'o' 、 'u' 。
- 解题思路:维护一个长度为 k 的滑动窗口,统计窗口内元音的个数。每次窗口滑动时,移除左边离开窗口的字符(判断是否为元音,若是则从计数中减去),加入右边新进入窗口的字符(判断是否为元音,若是则增加计数) ,记录过程中的最大元音个数。
- 题目1343. 大小为 K 且平均值大于等于阈值的子数组数目
- 题目描述:给你一个整数数组 arr 和两个整数 k 、 threshold 。请你返回长度为 k 且平均值大于等于 threshold 的子数组数目。
- 解题思路:先计算初始窗口(前 k 个元素)的和,判断其平均值是否满足条件,若满足则结果加1。之后滑动窗口,每次窗口滑动时,减去左边离开窗口的元素,加上右边新进入窗口的元素,重新判断窗口内元素的平均值是否满足条件,若满足则结果加1。
- 题目2090. 半径为 k 的子数组平均值
- 题目描述:给你一个整数数组 nums 和一个整数 k 。对于每个满足 i - k >= 0 且 i + k < n 的下标 i , 计算 (nums[i - k] + nums[i - k + 1] + ... + nums[i + k - 1] + nums[i + k]) / (2 * k + 1) 的值,生成结果数组。如果无法计算这个平均值,则结果数组中的该元素为 -1 。
- 解题思路:先计算初始窗口(长度为 2 * k + 1 )的和,填充对应位置的平均值。之后滑动窗口,每次减去左边离开窗口的元素,加上右边新进入窗口的元素,重新计算并更新对应位置的平均值。
2. 可变大小滑动窗口
- 题目3. 无重复字符的最长子串
- 题目描述:给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
- 解题思路:使用哈希表记录字符最后一次出现的位置。通过左右指针维护滑动窗口,右指针不断右移,当遇到重复字符时,左指针移动到重复字符下一次出现位置的下一位,每次移动更新最长子串的长度。
- 题目76. 最小覆盖子串
- 题目描述:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
- 解题思路:用哈希表 needs 记录 t 中每个字符及其需要的数量。通过左右指针维护滑动窗口,右指针不断右移扩大窗口,当窗口内包含 t 的所有字符时,尝试移动左指针缩小窗口,同时更新最小覆盖子串的长度,直到不能再缩小为止。
- 题目438. 找到字符串中所有字母异位词
- 题目描述:给定两个字符串 s 和 p ,找到 s 中所有 p 的异位词的子串,返回这些子串的起始索引。异位词指由相同字母重排列形成的字符串(包括相同的字符串)。
- 解题思路:使用两个哈希表,一个记录 p 中字符及其出现次数,另一个记录当前滑动窗口中字符及其出现次数。通过滑动窗口在 s 中移动,当窗口内字符的出现次数与 p 中字符的出现次数完全匹配时,记录当前窗口的起始索引。
3. 滑动窗口与其他数据结构结合
- 题目239. 滑动窗口最大值
- 题目描述:给你一个整数数组 nums ,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
- 解题思路:使用单调队列。单调队列中维护窗口内的元素,并且队头元素始终是窗口内的最大值。当窗口滑动时,移除不在窗口内的元素,同时保持队列的单调性(如果新元素大于队列尾部元素,则移除队列尾部元素,直到不满足该条件)。
- 题目643. 子数组最大平均数 I
- 题目描述:给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数。
- 解题思路:可以先计算初始窗口(前 k 个元素)的和,得到第一个平均数。之后滑动窗口,每次减去左边离开窗口的元素,加上右边新进入窗口的元素,通过新的和计算新的平均数,记录过程中的最大平均数。
4. 其他类型
- 题目2024. 考试的最大困扰度
- 题目描述:老师出一场由 n 道判断题构成的考试,答案为 'T' 或 'F' 。可以进行最多 k 次操作,每次操作可将一个答案改为 'T' 或 'F' 。要求返回在不超过 k 次操作的情况下,最大连续 'T' 或者 'F' 的数目。
- 解题思路:通过滑动窗口,分别考虑将 F 变成 T 和将 T 变成 F 的情况。在窗口滑动过程中,统计窗口内需要改变的字符数量(即与窗口内较多字符不同的字符数量 ),如果该数量不超过 k ,则尝试更新最大连续字符的长度;否则移动左指针缩小窗口。
- 题目904. 水果成篮
- 题目描述:农场的一排果树用整数数组 fruits 表示水果种类,只有两个篮子,每个篮子只能装单一类型水果,从任意一棵树开始采摘,每棵树摘一个水果,水果不符合篮子类型时停止采摘,返回可以收集的水果的最大数目。
- 解题思路:使用滑动窗口,窗口内最多包含两种水果类型。右指针移动扩大窗口,当窗口内出现三种水果类型时,左指针移动缩小窗口,直到窗口内恢复为最多两种水果类型,记录过程中窗口的最大长度。