滑动窗口
题目一:
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
- 输入:s = 7, nums = [2,3,1,2,4,3]
- 输出:2
- 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
提示:
- 1 <= target <= 10^9
- 1 <= nums.length <= 10^5
- 1 <= nums[i] <= 10^5
解法一:暴力解法
int minSubArrayLen(int target, int* nums, int numsSize) {
int i,j,sum=0,count=0,n=0;
int len=INT_MAX; //设置len为最大值,用于检验是否被赋值
for(i=0;i<numsSize;i++){
sum=0;
for(j=i;j<numsSize;j++){
sum+=nums[j];
if(sum>=target)//发现更小的len则更新长度
{
count=j-i+1;//子序列的长度
len=count>len?len:count;
break;
}
}
}
return len == INT32_MAX ? 0 : len;
}
- 时间复杂度 O(n^2)
- 空间复杂度 O(1)
解法二:滑动窗口
基本概念
滑动窗口是一种基于双指针的思想,想过两个指针指向的元素之间形成的一个窗口。就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
分类
窗口共分为两类,一是固定大小的窗口,二是大小动态变化的窗口。
应用情景
1.数据结构为数组和字符串
2.求某一个子串或者子序列最长最短等问题/求某一个目标值
3.该题目可通过暴力破解
滑动窗口的优点
可显著减少操作次数,减少时间复杂度
思路
用两个指针一个代表起始位置和终止位置,终止位置的指针的指向不断向后移动,判断其是否大于等等于s,若大于等于s,终止位置的指针不动,起始位置的指针向前移动,再次判断是否大于等于s,当不满足大于等于s的条件,将上一个起止的长度存储进length中。
用给出的样例为例子,我们来简单模拟滑动窗口的运行过程
1.初始时,用两个指针一个代表起始位置和终止位置,窗口为[开始,终止),左闭右开。
2.用代表终止位置的指针遍历数组,找到满足大于等于s的子数组,形成窗口
3.起点开始向后移动,再进行判断大小,更新窗口的数据
4.若符合条件则起点继续向右移动,直到不满足题目条件,将符合子数组长度储存进入length
5.当不满足题目条件,起始指针停止后,终止指针继续向右移动,返回进行第二步
6.最后直到找到符合题目条件的最短子数组
代码实现
int minSubArrayLen(int target, int* nums, int numsSize) {
int i,j=0,sum=0,result=INT_MAX,len;
for(i=0;i<numsSize;i++)//终止位置前移
{
sum+=nums[i];//找到大于等于的s数组
while(sum>=target){
len=i-j+1;//求出数组长度
result=result>len?len:result;
sum-=nums[j];
j++;//起始位置前移
}
}
return result == INT_MAX ? 0 : result;//最终返回输出结果
}
题目二:
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits
表示,其中 fruits[i]
是第 i
棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits
,返回你可以收集的水果的 最大 数目。
示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。
示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。
提示:
-
1 <= fruits.length <= 105
-
0 <= fruits[i] < fruits.length
解法思路:
题意:找出数组中个数最多,由两个数字组成的连续子数组
1.设置一左一右两个指针,right表示采摘到了那一棵树确定,left表示将两篮中的一篮水果清空的位置,双指针
2.确定左右指针的位置之后,开始用while循环清点果篮中水果的个数,与max比较,符合条件后存入max
3.将一个果篮清空,找到下一个符合条件的连续子数组,直至最后一棵果树
代码实现
int totalFruit(int* fruits, int fruitsSize) {
int left=0,right=0;
int count=0;
int a[100000]={0};
int max=0;
while(right<fruitsSize){
if(a[fruits[right]]==0)//计算水果种类,找到新水果
{
count++;
}
a[fruits[right]]++;//水果种类加一
while(count>2)//当水果种类超出两种,进入循环缩小窗口
{
a[fruits[left]]--;//缩小窗口,将先前两个篮子中的一个清空
if(a[fruits[left]]==0){
count--;
}
left++;//统计水果的数量
}
max = (right - left + 1) > max ? (right - left + 1) : max;//比较得到最大值
right++;//指针right向右移动
}
return max;
}