文章前面总结性概括了一些模版,后面有案例去套用模版。
食用方法推荐:先浏览一遍总结,心里有个底,然后去看案例如何应用模版解题,最后再看遍总结深刻体会一下。
【总结】
适合用滑动窗口解决的问题的前提:连续的子数组,字串,子序列。要连续。
四步走:
Step 1: 定义需要维护的变量们 (对于滑动窗口类题目,这些变量通常是最小长度,最大长度,或者哈希表)
Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口
start = 0;
end 是for i
里的i
Step 3: 更新需要维护的变量, 有的变量需要一个if
语句来维护 (比如最大最小长度)
Step 4 - 情况1 长度固定。if
Step 4 - 情况1 长度不固定。while 不合法
step 5 返回
class Solution:
def problemName(self, s: str) -> int:
# Step 1: 定义需要维护的变量们 (对于滑动窗口类题目,这些变量通常是最小长度,最大长度,或者哈希表)
x, y = ..., ...
# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口
start = 0
for end in range(len(s)):
# Step 3: 更新需要维护的变量, 有的变量需要一个if语句来维护 (比如最大最小长度)
x = new_x
if condition:
y = new_y
'''
------------- 下面是两种情况,读者请根据题意二选1 -------------
'''
# Step 4 - 情况1
# 如果题目的窗口长度固定:用一个if语句判断一下当前窗口长度是否达到了限定长度
# 如果达到了,窗口左指针前移一个单位,从而保证下一次右指针右移时,窗口长度保持不变,
# 左指针移动之前, 先更新Step 1定义的(部分或所有)维护变量
if 窗口长度达到了限定长度:
# 更新 (部分或所有) 维护变量
# 窗口左指针前移一个单位保证下一次右指针右移时窗口长度保持不变
# Step 4 - 情况2
# 如果题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题
# 如果当前窗口不合法时, 用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法
# 在左指针移动之前更新Step 1定义的(部分或所有)维护变量
while 不合法:
# 更新 (部分或所有) 维护变量
# 不断移动窗口左指针直到窗口再次合法
# Step 5: 返回答案
return ...
643.子数组最大平均数
给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。
请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。
示例 1:
输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
思路:
维护的本质上就是滑动窗口里的子数组。右指针向前为了寻找平均数最大,左指针向前为了符合要求:长度为k。
需要维护的变量是sum
两端for
- 更新变量:只有长度达到要求时才进行变量的更新
- 长度的计算是right-left+1;
- 长度固定:要用if进行左指针的移动,使得右指针能够继续向前移动,而长度保持不变。
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var findMaxAverage = function(nums, k) {
//定义需要维护的变量
let res = -Infinity;
let sum = 0;
//定义首尾端
let left = 0;
for(let i = 0;i<nums.length;i++){
//随着右端的移动,更新维护的变量
sum = sum+nums[i];
// 长度固定,if语句
if(i-left+1===k){
// 在左指针右移前更新所有step1定义的变量。
res = Math.max(sum,res);
sum = sum-nums[left];
left++;
}
}
// 返回结果
return res/k;
};
3.请你找出其中不含有重复字符的 最长子串 的长度。
最长子串 :
最长子数组
最长子序列 不要求连续
要连续的就用滑动窗口。
维护的变量是字符串的长度、哈希表、最大长度(满足条件才能更新)
本质上就是滑动窗口里的字符串。右指针向前为了寻找最长,左指针向前为了符合要求:不含有重复的。
首尾
-
更新维护的变量 要满足条件,维护的变量有两个,一个是哈希表,一个是长度,哈希表随着for维护,然后最大长度要在满足哈希表中没有重复字符的条件下进行维护。
- 哈希表中没有重复字符就是每个字符的长度都为1,就是map.size() = left - right+1
-
长度不固定,所以用while 不合法剔除。
- 不合法的情况,就是长度
function(String s){ // 窗口中的字符串:用哈希表保存,通过map.size和right - left +1 之间的关系去判断这是不是一个重复的字符串。 //定义需要维护的变量 let len = 0; let map = new Map(); //定义首尾端 let left = 0; for (let i = 0; i < s.length; i++) { //随着右端的移动,更新维护的变量 let vale = map.has(s[i]) ? map.get(s[i]) : 0; map.set(s[i], vale + 1); //step 3 有的变量需要通过条件语句来维护 if (i - left + 1 === map.size) { len = Math.max(len, map.size); } // step 4 长度不固定,需要通过while 来排除不合法 while (i - left + 1 > map.size) { let temp = s[left]; let value = map.get(temp); // 在左指针右移前更新所有step1定义的变量哈希表。 if (value === 1) { map.delete(temp); } else { value--; map.set(temp, value) } left++; } len = Math.max(map.size, len); } return len; }
209. 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
本质上就是维护滑动窗口里的子数组。右指针向前为了寻找和最大(>= target),左指针向前为了符合要求:长度最小。
function(target,nums){
let len = Infinity;
let sum = 0;
let left = 0;
for(let i = 0;i<nums.length;i++){
sum = sum + nums[i];
while(sum>=target){
sum=sum-nums[left];
len = Math.min(len,i-left+1);
left++;
}
}
if(len === Infinity){
return 0;
}
return len;
}