1.滑动窗口的思想
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果。这是数组操作中的一个重要的方法。
2. 滑动窗口相关例题
2.1 长度最小的子数组
这道例题的核心就在于通过双指针不断调节窗口的起始位置
class Solution {
//移动窗口:1.两个指针
//2.核心思路:在于调整滑动窗口起始的位置
public int minSubArrayLen(int target, int[] nums) {
int sum =0;//子数组的和
int left =0;//定义滑动窗口起始位置
int result = Integer.MAX_VALUE;
for(int right =0;right<nums.length;right++){
sum+=nums[right];//记录子数组的和
while(sum>=target){
result=Math.min(result,right-left+1);//取最小值
sum-=nums[left++];//从左向右减小
}
}
return result==Integer.MAX_VALUE?0:result;
}
}
2.2 水果成篮
本题的重点在于创建一个新数组,用来嵌套原数组,用这种方法来进行数组操作。这也算是一个小技巧吧。
思路:
1.当没有采摘过当前水果时,count记录,更新水果的尾部.(每次添加一种新水果都要更新尾部)
2.当采摘了两种水果时,更新此时的最大数量。
3.当超过两种水果时,清空第一种水果,并且将首部右移。
4.一定要注意更新水果的尾部(容易忽略)…
class Solution {
/*
* 测试用例(直观一点较好):1,2,3,2,3,1
*/
public int totalFruit(int[] fruits) {
int left = 0;
int count = 0;//记录共有几种水果
int len = fruits.length;
int [] nums =new int[len];//新数组,用来存放水果
int ans = 0;//可以收集水果的最大数量
int renewLeft = 0;//用来更新水果的尾端
for(int right=0;right<len;right++){
//第一次采摘该种类的水果
if(nums[fruits[right]]==0){
count++;
renewLeft=right;
}
nums[fruits[right]]+=1;//更新该种水果的数量
//更新可以收集水果的最大数目
if(count==2){
ans= ans>(nums[fruits[left]]+nums[fruits[renewLeft]])?
ans: nums[fruits[left]]+nums[fruits[renewLeft]];
}
// 当超过两种水果时,清空第一种水果
while(count>2){
nums[fruits[left]]--;//减少第一种水果的数量
if(nums[fruits[left]]==0) count--;//直到第一种水果清空,最大数目减一
left++;
}
}
if(count==1) return nums[fruits[left]]; //如果只有一种水果
return ans;
}
}
2.3 最小覆盖子串
思路:
1.创建一个新数组用来进行操作数据,与上一个例题同
2.首先遍历字符串t,得到我们所需要的字符的种类与个数
3.遍历字符串s,筛选出我们所需的字符(即该元素数量大于0),并且每筛选出一个就将该元素的数量减一。直至我们筛选出所有的所需元素。那么,这个时候新的问题又产生了,我们无法保证该子序列只包含我们所需的元素。
4.因此我们需要筛选出多余的元素(该元素数量小于0,说明1.我们需要该元素,但是该元素的数量过多,2.我们不需要这种元素。)。因此,我们将串口缩小,同时每找到一个新元素,就将该元素数量加一,用来作为循环是否结束的标准。并且将左窗口不断右移动。
5.确定好新的窗口 ,在每个新的窗口中找最小的窗口,即输出长度
6.开始下一个窗口的寻找 这时候left指向的s字串 是t的第一个字符。我们要看剩下的字串中有没有满足的, 首先让当前指向的字符不算,然后指针右移。
7.至此为止,便是我们一个循环结束。。。。。
class Solution {
public String minWindow(String s, String t) {
if (s == null || t.length() == 0 ) {
return "";
}
// 将需要的字符个数记录下(t)
int[] window = new int[128];
for (int i = 0; i < t.length(); i++) {
window[t.charAt(i)] += 1;
}
int right = 0, left = 0, count = t.length(), size = Integer.MAX_VALUE, start = 0;
// 扩大窗口,先找到满足含有t s的字串
while (right < s.length()) {
// window里>=0都是需要的count,<0的都是非必要字符
char c = s.charAt(right);
// 判断窗口是否需要当前元素,需要的话count-1
if (window[c] > 0) {
count--;
}
window[c] -= 1; // 更新想要得到的窗口
// 当count=0 说明需要的字符都被找到了,左指针右移 缩小窗口,直到刚好满足count==0
if (count == 0) {
while (window[s.charAt(left)] < 0) {
window[s.charAt(left)]++;
left++;
}
// 确定好新的窗口 在每个新的窗口中 找最小的窗口
if (right - left < size) {
// size指向下标为确定好窗口的最后一个字符
size = right - left;
start = left;
}
// 开始下一个窗口的寻找 这时候left指向的s字串 是t的第一个字符
// 我们要看剩下的字串中有没有满足的, 首先让当前指向的字符不算,然后指针右移
window[s.charAt(left)]++;
left++;
count++;
}
right++; // 右指针不断右移,窗口不断扩大
}
return size == Integer.MAX_VALUE ? "" : s.substring(start, start + size + 1);
}
}