滑动窗口
算法主要思想
用左右指针维护一个窗口(连续的子数组/子串),根据题目在遍历数组或者字符串的时候动态调整两个指针(一般都是++),遇到可行解就进行记录。
let len = s.length;//范围
let left = 0, right = 0;
while(right < len){
...未达到要求时
right++;//扩张
while(条件){
...达到要求时进行一个可行解的计算
left++;//收缩
}
}
leetcode题目
209 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/mi… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
求最短的和>=s的子数组,子数组一定是连续的,所以可以用一个窗口表示这个连续的子数组,[left...right]代表这个无重复的数组。 在遍历数组的时候,如果和<s就right++扩张右边界,如果和>=s就记录长度,然后left++收缩左边界,最后求这个窗口的最小长度即可。
/**
* @param {number} s
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(s, nums) {
let min = Infinity;//定义为最大值
let right = left = 0;
let len = nums.length;
if(len===0)//如果数组为空就直接返回0
return 0;
let sum = nums[0];
while(right < nums.length){
if(sum >= s){//如果和>=s
min = Math.min(right - left + 1,min);
sum -= nums[left];
left++;
}else{//如果不符合s
if(++right === len)//以防溢出
break;
sum += nums[right];
}
}
min = (min===Infinity)?0:min;//如果没有合适的解就返回0
return min;
};
3 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3
输入: s = "bbbbb"
输出: 1
输入: s = "pwwkew"
输出: 3
输入: s = ""
输出: 0
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/lo… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
求最长的无重复字符的子串,子串一定是连续的,所以可以用一个窗口表示这个连续的子串,[left...right]代表这个无重复的子串。在遍历字符串的时候,如果遇到不重复的字符就right++扩张右边界,如果遇到相同的字符就left++收缩左边界到那个被重复的字符之后。求这个窗口的最大值即可。
/**
* @param {string} s
* @return {number}
*/
let lengthOfLongestSubstring = function(s) {
let maxV = 0;
let map = {};//用于存储对应字符出现的频率
let left = 0;
let right = -1;//右边界初始值设为-1
while(left < s.length){
let next = s[right+1];
if(next!=undefined && !map[next]){//遇到不重复的字符右边界扩张
map[next] = 1;
right++;//不重复子串是[left...right]
}else{//遇到重复的字符 左边界收缩到相同字符 (每次收缩一个字符,但由于没有到达相同的字
//符所以一直会走else分支)
map[s[left]] = 0;
left++;
}
maxV = Math.max(right - left + 1,maxV);
}
return maxV;
};
438 找到字符串中所有字母异位词
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明: 字母异位词指字母相同,但排列不同的字符串。 不考虑答案输出的顺序。
输入:
s: "cbaebabacd" p: "abc"
输出:
[0, 6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/fi… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路1:
用两个指针确定左右边界,然后判断窗口中的字符串是否和目标字符串匹配,若果匹配,左边界存储在数组中,最后进行输出。 复杂度主要在于,每次都要记录窗口中的字母出现的频率,然后和目标字符串进行比较。
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
let findAnagrams = function(s, p) {
let len = p.length;
let map = {};
for(let i = 0; i < len; i++){
if(!map[p[i]]){
map[p[i]] = 1;
}else{
map[p[i]]++;
}
}
let res = [];
let left = 0;
let right = len - 1;
while(right < s.length){
if(isSame(s,map,left,right)){
res.push(left);
}
left++;
right++;
}
return res;
};
//判断s[left,right]和p是否是字母异位词
let isSame = function(s,map,left,right){
let smap = {};//存储窗口中的值
for(let i = left; i <= right; i++){
if(!smap[s[i]]){
smap[s[i]] = 1;
}else{
smap[s[i]]++;
}
}
let flag = true;
let keys = Object.keys(map);//获取目标map中所有键
for(let key of keys){
if(!smap[key] || smap[key] != map[key]){
flag = false;
break;
}
}
return flag;
}
解题思路2:
用滑动窗口的模板
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
let findAnagrams = function(s, p) {
let targetMap = {};
let winMap = {};
for(let i = 0; i < p.length; i++){
if(!targetMap[p[i]]){
targetMap[p[i]] = 1;
winMap[p[i]] = 0;
}else{
targetMap[p[i]]++;
}
}
let left = 0,right = 0;
let match = 0;
let keyLength = Object.keys(targetMap).length;
let res = [];
while(right < s.length){
if(targetMap[s[right]]){//如果当前字母在目标字符串里
winMap[s[right]]++;//频率+1
if(winMap[s[right]]===targetMap[s[right]]){//频率相等
match++;
}
}
right++;
while(match === keyLength){
if(right - left === p.length){
res.push(left);
}
if(targetMap[s[left]]){
winMap[s[left]]--;
if(winMap[s[left]] < targetMap[s[left]])
match--;
}
left++;
}
}
return res;
}
76 最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
输入:s = "a", t = "a"
输出:"a"
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/mi… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
/**
* @param {string} s
* @param {string} t
* @return {string}
**/
var minWindow = function(s, t) {
let left = 0;
let right = 0;
let targetMap = {};
let winMap = {};
//记录目标字符串的字符频率
for(let i = 0; i < t.length; i++){
if(!targetMap[t[i]]){
targetMap[t[i]] = 1;
winMap[t[i]] = 0;
}else{
targetMap[t[i]]++;
}
}
let minLength = s.length;
let flag = false;//表示是否存在覆盖所有子串的串
let match = 0;
let minl = 0, minr = 0;
let keyLength = Object.keys(targetMap).length;//表示一共要匹配多少种字母
while(right < s.length){
if(targetMap[s[right]]){//有相同的字母时
winMap[s[right]]++;
if(winMap[s[right]] === targetMap[s[right]]){
match++;
}
}
right++;//向右扩张
while(match === keyLength){//出现了可行解
flag = true;
if(minLength >= right - left){//判断是否是比之前的更优解
minl = left;
minr = right;
minLength = right - left;
}
//向右收缩
if(targetMap[s[left]]){//如果碰到了相应的字母 收缩后就可能不是可行解了
winMap[s[left]]--;
if(winMap[s[left]] < targetMap[s[left]]){
match--;
}
}
left++;
}
}
return flag? s.slice(minl,minr) : "";
};
239 滑动窗口的最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/sl… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路1:
暴力解法,用两个指针确定左右边界,然后对其中的子数组使用Math.max()求出最大值,存储在数组中,最后进行输出。时间复杂度是O(nk)
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
let maxSlidingWindow = function(nums, k) {
let res = [];
if(nums.length < k){
res.push(max(nums,0,nums.length));
return res;
}
for(let left = 0, right = left + k; right <= nums.length; left++,right++){
res.push(max(nums,left,right));
}
return res;
};
let max = function(nums,l,r){
let max = -Infinity;
for(let i = l; i < r; i++){
//max = (max > nums[i]) ? max : nums[i]; //这种写法会超时
max = Math.max(max,nums[i]);
}
return max;
}
TODO:解题思路2:
单调队列解法