1. 什么是滑动窗口算法?
滑动窗口算法使用两个指针(通常称为左指针和右指针)来表示当前窗口的边界。在遍历过程中,根据问题的要求和窗口内的状态调整窗口的大小和位置。算法的基本思想是:通过移动窗口的左右边界来维护一个满足特定条件的子区间,并在每次窗口滑动时更新结果。
2. 滑动窗口算法相关题目解题步骤?
滑动窗口算法题型一般是有套路的,具体如下:
- 初始化窗口:设置左指针和右指针的初始位置,以及一个变量来跟踪满足条件的子序列。
- 移动右指针:增加右指针,扩大窗口,直到满足题目要求。
- 判断并更新结果:在满足题目要求的情况下,更新结果变量。
- 移动左指针:收缩窗口,即移动左指针,直到窗口内不再满足题目要求。
- 重复步骤2-4:直到右指针遍历完整个序列。
3. 相关题目
方法一:
这道题我首先用暴力去做,就是双层for循环,第一层循环从数组的每个位置开始,然后第二层循环就从它的后面开始加,直到加到大于等于target为止。这样做就是纯暴力,结果肯定没问题,但是必然超时,所以不推荐。
public int minSubArrayLen(int target, int[] nums) {
if(nums.length == 1 && nums[0] < target) {
return 0;
}
if(nums.length == 1 && nums[0] >= target) {
return 1;
}
int sum = 0;
int res = Integer.MAX_VALUE;
for(int i = 0; i < nums.length; i++) {
sum = nums[i];
if(sum >= target) {
return 1;
}
for(int j = i + 1; j < nums.length; j++) {
sum += nums[j];
if(sum >= target) {
res = Math.min(res, j - i + 1);
break;
}
}
sum = 0;
}
return res == Integer.MAX_VALUE ? 0 : res;
}
方法二:前缀和+二分查找。
如果看过我前面文章的小伙伴,一看到这道题的题干就应该想到用前缀和了,有关子数组的问题一般都可以用前缀和来求解。具体解法,请移步【刷题笔记】前缀和_什么是前缀和-CSDN博客
方法二:滑动窗口
这道题最好的解法应该是滑动窗口,其实就是定义两个指针,左指针先指向第一个值,让右指针去遍历数组,当求得得和大于等于target得时候,就可以记录这时候子数组得长度了,但是要注意一种情况:
例:假设给定数组如下,目标值为7。
可见我们先循环找到了{1,2,3,4}符合条件,但是这样就可以返回子数组长度是3了吗?
显然不是!因为我们可以发现{2,3,4}和{3,4}也是满足条件的,因此未来获取最短的子数组,我们还得让left向右移动,然后找到{2,3,4},更新最小子数组长度;再往右移动,找到{3,4},继续更新最小子数组长度;在移动,发现不满足条件了,好left不动了,继续让right去找满足条件的组合,如此循环。
代码详解:
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int res = Integer.MAX_VALUE;
int sum = 0;
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= target) {
res = Math.min(res, right - left + 1);
sum -= nums[left];
left++;
}
}
return res == Integer.MAX_VALUE ? 0 : res;
}
这道题我是采用hashmap+滑动窗口的方式进行求解的。首先,就是我们让右指针去遍历数组,直到找到水果的种类超过2个,这个时候在让left去移动,直到让map中的水果种类小于等于2种。这里说一下,为什么map可以,而set不行:
例:
当我们遍历到这个位置的时候,我们找到了{1,4,1},可见如果想继续往下走,需要把“1”和“4”去掉,但是第二个“1”得保留,因为要和后面的数字组成{1,2,2,2},但是如果你用set的话,我们主要是想在set中把“4”给他移除掉,让set里面只剩两个元素,在left走过“4”之前,要先走过1个“1”,但是我们不能确定要走几个“1”才能让“4”的个数为0。然而,如果使用map的话,我们可以把让key为fruits[i],而value则为fruits[i]的个数,这样的话我们就可以随时记录,当“4”的value为0时,left就可以停了。
代码详解:
public int totalFruit(int[] fruits) {
int left = 0;
Map<Integer, Integer> map = new HashMap<>();
int res = 0;
for (int right = 0; right < fruits.length; right++) {
map.put(fruits[right], map.getOrDefault(fruits[right], 0) + 1);
while (map.size() > 2) {
map.put(fruits[left], map.get(fruits[left]) - 1);
if (map.get(fruits[left]) == 0) {
map.remove(fruits[left]);
}
left++;
}
res = Math.max(right - left + 1, res);
}
return res;
}