滑动窗口(Sliding Window)
学过计算机网络的同学对于“滑动窗口”这个名词一定不会陌生,在计网中利用到滑动窗口的思想实现了网络中数据传输的流量控制、差错控制等等…
同样在刷题中,滑动窗口也是一种重要的思想,用来解决字串,子数组的问题。在使用滑动窗口解决问题时,始终会维护两个指针left和right分别指向当前窗口的左边界与右边界,每次移动左指针或者右指针,移动之后判断窗口之内的元素是否满足要求。
什么时候使用滑动窗口?
当题目给出的输入是一个字符串或者一个数组,所求解是该字符串或数组的一部分时,通常你就需要考虑使用滑动窗口来解决该问题.
下面给出几个leectcode中关于滑动窗口的例题,我只给出了代码实现,想要看题目以及详细解答的可以移步leetcode
lc209 长度最小的子数组 (mid)
//209
public class 长度最小的子数组 {
//利用滑动窗口的思想寻找符合要求的子数组
//当数组中的元素值的和小于target的时候移动右指针,容纳更多数入数组
//当数组中的元素值的和大于target的时候移动左指针,减少数组中元素的个数,缩短数组的长度
public int minSubArrayLen(int target, int[] nums){
int left=0;
int right=-1;
int minLength=nums.length+1;
int currLength=nums.length+1;
int sum=0;
while(left<nums.length){
if(right<(nums.length-1) && sum<target){
sum+=nums[++right];
}else{
sum-=nums[left++];
}
if(sum>=target){
currLength=right-left+1;
minLength=currLength<minLength ? currLength : minLength;
}
}
if(minLength==(nums.length+1))
return -1;
return minLength;
}
}
lc3 无重复字符的最长子串 (mid)
public class 无重复字符的最长字串 {
public int lengthOfLongestSubstring(String s) {
int left=0;
int right=-1;
int maxLen=0;
Map<Character,Integer> map=new HashMap<>();
while(right<s.length()-1){
if(!map.containsKey(s.charAt(right+1))){
map.put(s.charAt(right+1),1);
right++;
int len=right-left+1;
if(maxLen<len)
maxLen=len;
}else{
map.remove(s.charAt(left));
left++;
}
}
return maxLen;
}
}
lc438 找到字符串中所有字母异位词 (mid)
public class 找到字符串中所有字母异位词 {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> list=new LinkedList<>();
if(s.length()<p.length())
return list;
int left=0;
int right=p.length()-1;
while(right<s.length()){
if(isAnagrams(s.substring(left,right+1),p)){
list.add(left);
}
left++;
right++;
}
return list;
}
public boolean isAnagrams(String a,String b){
int[] arrA=new int[26];
int[] arrB=new int[26];
for(int i=0;i<a.length();i++){
int index=a.charAt(i)-'a';
arrA[index]++;
}
for(int i=0;i<b.length();i++){
int index=b.charAt(i)-'a';
arrB[index]++;
}
if(Arrays.equals(arrA,arrB))
return true;
else
return false;
}
}
lc76 最小覆盖子串 (hard)
class Solution {
public String minWindow(String s, String t) {
if(s.length()<t.length())
return "";
int sLen=s.length();
int tLen=t.length();
int minLen=sLen+1; //设置一个不可能到达的初始值
int resLeft=0;
int resRight=0;
int[] need=new int[128]; //记录还需要哪些字母才能覆盖t字符串,数值>0时表示还需要,<0时表示有多余的该字母
int needCount=tLen; //记录所需字母的总数
int left=0; //字串范围[left,right)
int right=0;
//通过遍历字符串t,初始化need数组
for(int i=0;i<tLen;i++){
need[t.charAt(i)]++;
}
while(right<sLen){ //循环结束条件,直到右指针走到s末尾
//当needCount大于0时移动右指针
while(needCount>0 && right<sLen){
if(need[s.charAt(right)]>0) //仅对t中需要的字母更新needCount
needCount--;
need[s.charAt(right)]--;
right++;
}
if(needCount!=0) //右指针走到最后的时候都没找到满足条件的字串,则直接退出循环
break;
//当needCount等于0时移动左指针,寻找满足要求最短的字串
while(needCount==0 && left<right){
need[s.charAt(left)]++;
if(need[s.charAt(left)]>0)
needCount++;
left++;
}
int currLen=right-left+1;
if(currLen<minLen){
minLen=currLen;
resLeft=left-1;
resRight=right-1;
}
}
if(minLen==sLen+1)
return "";
return s.substring(resLeft,resRight+1);
}
}