所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢。
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
此时难免再次陷入 暴力解法的怪圈。
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最小长度。
while j < len(nums):
判断[i, j]是否满足条件
while 满足条件:
不断更新结果(注意在while内更新!)
i += 1 (最大程度的压缩i,使得滑窗尽可能的小)
j += 1
给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最大长度。
while j < len(nums):
判断[i, j]是否满足条件
while 不满足条件:
i += 1 (最保守的压缩i,一旦满足条件了就退出压缩i的过程,使得滑窗尽可能的大)
不断更新结果(注意在while外更新!)
j += 1
关键的区别在于,最大滑窗是在迭代右移右边界的过程中更新结果,而最小滑窗是在迭代右移左边界的过程中更新结果。因此虽然都是滑窗,但是两者的模板和对应的贪心思路并不一样,而真正理解后就可以在lc.76,lc.904,lc.3, lc.1004写出非常无脑的代码。
时间复杂度为:O(N), 空间复杂度为:O(N).
其实双指针和滑动窗口是有些许区别的。滑动窗口一句话就是右指针先出发,左指针视情况追赶右指针。可类比男生暗恋女生,两人都在往前走,但男生总是默默跟着女生走但又不敢超过她。因此,右指针最多遍历一遍数组,左指针也最多遍历一次数组,时间复杂度不超过O(2N)。接下来,如何判断滑动窗口内是否满足题设条件,有两种选择:(1) 要么你遍历这个滑窗,通过遍历来断滑窗是否满足需要O(N), 那么总的时间就退化为O(N^2), (2) 要么你选择字典,用空间换时间,那么判断划窗是否满足条件则需要 O(1),总时间为O(N).
白话题意:求满足某个条件(数组值最多就两类的连续数组,例如[1,2,2,1,2])的最长数组长度
public int totalFruit(int[] fruits) {
int n=fruits.length;
Map cnt=new HashMap<>();
int left=0,ans=0;
for (int right = 0; right < n; right++) {
cnt.put(fruits[right],cnt.getOrDefault(fruits[right],0)+1);
while (cnt.size()>2){
cnt.put(fruits[left],cnt.get(fruits[left])-1);
if (cnt.get(fruits[left])==0){
cnt.remove(fruits[left]);
}
left++;
}
ans=Math.max(ans,right-left+1);
}
return ans;
}
public String minWindow(String s, String t) {
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen){
return "";
}
char[] sCharArray = s.toCharArray();
char[] tCharArray = t.toCharArray();
// ascii('z') = 122,存字母对应的ASCII码
int[] winFreq = new int[128]; //用来记录窗口内(也就是当前子串)的字符情况
int[] tFreq = new int[128]; //用来记录t字符的频数
for (char c : tCharArray){
tFreq[c]++; //桶排序中统计出现次数的思想
}
//滑动窗口内部包含多少T中的字符,对应字符频数超过不重复计算
int distance = 0; //用来记录t中字符在窗口中出现的频数
int minLen = sLen + 1; //最小子串的长度,用于最后的substring
int begin = 0; //开始下标,用于最后的substring
//左右两个指针,也就是窗口的两个边界
int left = 0;
int right = 0;
//左闭右开区间[left,right)
while (right < sLen){ //当右指针没移动到头时
//右指针向右移动逻辑
char sCharRight = sCharArray[right]; //拿到当前右指针对应的字符
if (tFreq[sCharRight] == 0){ //右指针的字符不是t中字符时
right++; //向右移动
continue;
}
if (winFreq[sCharRight]