啥是滑动窗口,它能解决什么样的问题?
仰望天空,妳我亦是行人.✨
个人主页——微风撞见云的博客
数据结构与算法专栏的文章图文并茂生动形象简单易学!欢迎大家来踩踩~
希望本文能够给读者带来一定的帮助文章粗浅,敬请批评指正!
滑动窗口算法也是一种思想
,是双指针的拓展和延伸
。
滑动
:说明这个窗口是移动的,也就是移动是按照一定方向来的。
窗口
:窗口大小并不是固定的,可以不断扩容直到满足一定的条件;也可以不断缩小,直到找到一个满足条件的最小窗口;当然也可以是固定大小。
滑动窗口主要应用在数组和字符串上
,在一个特定大小的字符串或数组上进行操作,而不在整个字符串和数组上操作,这样就降低了问题的复杂度,从而也达到降低了循环的嵌套深度。
什么情况适合用滑动窗口来解决实际问题呢?
一般给出的数据结构是数组或者字符串
。
求取某个子串或者子序列
最长最短等最值问题或者求某个目标值时。
题目:长度最小的子数组
题目描述:
给定一个含有n个正整数的数组和一个正整数target 。
找出该数组中满足其和≥target的长度最小的连续子数组[nums l,nums l+1,…, nums r-1, nums r],并返>回其长度。如果不存在符合条件的子数组,返回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
上文提到:“滑动窗口算法也是一种思想
,是双指针的拓展和延伸
”。为了更加直观的体现这种思想,我决定用队列来模拟滑动窗口
,以队头和队尾作为双指针
来进行演示整个题解过程。
我们把数组中的元素不停的入队,直到总和大于等于target为止
,接着记录下队列中元素的个数,然后再不停的出队,直到队列中元素的和小于target为止
(如果不小于target,也要记录下队列中元素的个数,这个个数其实就是不小于target 的连续子数组长度,我们要记录最小
的即可)。接着再把数组中的元素添加到队列中......
重复上面的操作,直到数组中的元素全部使用完为止
。
这里以[2,3,1,2,4,3]举例画个图来看下:
双指针:
public class Main {
public static int minSubArrayLen(int target, int[] nums) {
int low = 0, high = 0;//low:队头 , high:队尾
int sum = 0, min = Integer.MAX_VALUE;
while (high < nums.length) {
sum += nums[high++];
while (sum >= target) {
min = Math.min(min, high - low);
sum -= nums[low++];
}
}
return min == Integer.MAX_VALUE ? 0 : min;
}
public static void main(String[] args) {
System.out.println(minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
}
}
队列:
public class Main {
public static int minSubArrayLen(int target, int[] nums) {
int num = 0;//计算子数组的累计和
int min = nums.length;//长度赋初值
boolean flag = false;//判断长度值是否改变过
Deque<Integer> deque = new ArrayDeque<>();//定义出能体现滑动窗口数据结构的数据结构————队列,这样的双端队列更加高效。
int index;//用于记录位置的指针
for (index = 0; index < nums.length; index++) {//设置滑动窗口的初始长度
if (num >= target) {
min = Math.min(min, deque.size());//保留之前值与当前值中的较小值
flag = true;
break;
}
deque.addLast(nums[index]);//向队列中添加数据nums[index]
num += nums[index];
}
//上面的循环用于初始化滑动窗口的长度,此时我们就已经有了一个长度为deque.size()的窗口了
while (num >= target || index < nums.length) {
/*此时deque中存放值的总和已经超过了目标值target,此时我们的窗口已经满足的滑动的条件,
如果现在num的值超过目标值就将前面先进入队列deque的值踢出,就相当于窗口就向后移动了一步 */
if (num >= target) {
min = Math.min(min, deque.size());
flag = true;
num -= deque.poll();
} else {//如果此时不满足num>=target条件就继续向后延伸,向队列deque中推入之后的值加到num中。
num += nums[index];
deque.addLast(nums[index++]);
}
}
if (!flag) return 0;
return min;
}
public static void main(String[] args) {
System.out.println(minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
}
}
初学一门技术时,总有些许的疑惑,别怕,它们是我们学习路上的点点繁星,帮助我们不断成长。
文章粗浅,希望对大家有帮助!
参考文章:滑动窗口算法 、滑动窗口2:滑动窗口算法基本原理、滑动窗口详解、长度最小的子数组