视频课程:同向双指针 滑动窗口【基础算法精讲 01】_哔哩哔哩_bilibili
滑动窗口也可以理解为双指针法的一种。
滑动窗口的核心在于连续!如果需要得到的结果是满足某种较为明显条件的连续的子数组,可以考虑使用滑动窗口来做。
通常情况下,滑动窗口是枚举右端点,然后按条件移动左端点。
枚举右端点比枚举左端点方便,因为枚举右端点,移动左端点时信息是枚举过的,是已知的;移动左端点是在缩小范围,通常更好写。
如果左端点的移动条件不明或者比较复杂,就不适合用滑动窗口。
滑动窗口一般来说,是将题目转换为求最短长度的子序列/最大长度的子序列问题,满足条件的子序列就是窗口,枚举所有窗口右端点的同时,更新左端点。
左端点的移动原则:找长度最短的窗口,就是只要满足条件就移动左端点;找长度最长的窗口,就是超范围了才移动左端点。
找长度最短的连续子序列
示例1:209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
- 1 <= target <= 10^9
- 1 <= nums.length <= 10^5
- 1 <= nums[i] <= 10^5
思路
本题是很经典的滑动窗口题目,选取特定条件下的连续子数组,且为找最小长度子序列问题。因此本题是只要while循环满足条件,就更新左端点,从而使得最后得到的窗口长度是最小值。
本题特定条件并不复杂,是求和的条件,并不需要单独写函数。只要满足特定条件,就可以进行滑动。
解答
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i=0;
int sum=0;
int subLength=0;
int result = INT_MAX;
//开始滑动,直接用[i,j]作为窗口
for(int j=0;j<nums.size();j++){
sum += nums[j];
//只要满足条件,就不断滑动,方便取最小值
while(sum>=target){
subLength = j-i+1;
result = result<subLength?result:subLength;
sum-=nums[i];
i++;
}
}
//如果一直没找到,结果置零
if(result==INT_MAX){
result=0;
}
return result;
}
};
二刷记录
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
//经典滑动窗口
int leftIndex=0;
int sum=0;
int subLength = 0;
int result = INT_MAX;
for(int i=0;i<nums.size();i++){
sum+=nums[i];
//rightIndex++;不需要单独的右端点,滑动窗口本身就是枚举右端点
if(sum<target) continue;
else{
while(sum>=target){
//满足条件才会更新subLength
subLength = i-leftIndex+1;//记录遍历过的所有的窗口长度
result = (result<subLength)?result:subLength;
sum-=nums[leftIndex];
leftIndex++;
}
}
}
return (result==INT_MAX)?0:result;
}
};
找最长的连续子序列
示例1:2779.数组的最大美丽值(转换成连续子序列最大长度)
给你一个下标从 0 开始的整数数组 nums
和一个 非负 整数 k
。
在一步操作中,你可以执行下述指令:
- 在范围
[0, nums.length - 1]
中选择一个 此前没有选过 的下标i
。 - 将
nums[i]
替换为范围[nums[i] - k, nums[i] + k]
内的任一整数。
数组的 美丽值 定义为数组中由相等元素组成的最长子序列的长度。
对数组 nums
执行上述操作任意次后,返回数组可能取得的 最大 美丽值。
注意: 你只能对每个下标执行 一次 此操作。
数组的 子序列 定义是:经由原数组删除一些元素(也可能不删除)得到的一个新数组,且在此过程中剩余元素的顺序不发生改变。
示例 1:
输入:nums = [4,6,1,2], k = 2
输出:3
解释:在这个示例中,我们执行下述操作:
- 选择下标 1 ,将其替换为 4(从范围 [4,8] 中选出),此时 nums = [4,4,1,2] 。
- 选择下标 3 ,将其替换为 4(从范围 [0,4] 中选出),此时 nums = [4,4,1,4] 。
执行上述操作后,数组的美丽值是 3(子序列由下标 0 、1 、3 对应的元素组成)。
可以证明 3 是我们可以得到的由相等元素组成的最长子序列长度。
示例 2:
输入:nums = [1,1,1,1], k = 10
输出:4
解释:在这个示例中,我们无需执行任何操作。
数组 nums 的美丽值是 4(整个数组)。
提示:
1 <= nums.length <= 105
0 <= nums[i], k <= 105
思路1:排序+滑动窗口
因为美丽值的定义是数组中由相等元素组成的最长子序列的长度,因此我们想要获得全部都是相等元素的序列,并求最大长度。
虽然本题要求 子序列 定义是剩余元素的顺序不发生改变,但是求的是相等元素组成的最长子序列长度,因此元素的顺序并不影响结果。
因此我们可以先进行排序,让相等的元素排在一起,此时只需要判断窗口左端点(最小值)和右端点(最大值)是不是相等,也就是只有nums[r]-k<=nums[l]+k
的时候,才作为有效窗口。<