给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
最直接的做法就是用双重循环,相当于内层循环每次都找出本次外循环变量位置之后的所有子数组,再比较其大小。
- function minSubArray(s, nums) {
- let result = Infinity;
- let sum = 0;
- let subLength = 0;
-
- for (let i = 0; i < nums.length; i++) {
- sum = 0;
- for (let j = i; j < nums.length; j++) {
- sum += nums[j];
- if (sum >= s) {
- subLength = j - i + 1;
- result = Math.min(result, subLength);
- break;
- }
- }
- }
- return result === Infinity ? 0 : result;
- }
这种解法的时间复杂度是O(n²),那么有没有一种办法用一次循环就遍历完整个数组,找到最小的数组长度呢,这种办法就是滑动窗口。
双重循环时,我们用一个for表示起始位置,另一个表示终止位置,在滑动窗口中,我们想用一重循环就完成这个任务,那么循环变量就必须能表示整个范围,也就是必须是可以表示终止位置,先来看一下代码。
- function minSubArray(s, nums) {
- let result = Infinity;
- let sum = 0;
- let subLength = 0;
- let i = 0;
- for (let j = 0; j < nums.length; j++) {
- sum += nums[j];
- //当和满足条件时 缩小窗口 直到不满足条件为止
- while (sum >= s) {
- //计算子串长度
- subLength = j - i + 1;
- // 将更小的赋值给result
- result = Math.min(result, subLength);
- // 当缩小窗口时 总和是不断减小的
- // 这种滑动窗口也被称为最小滑窗
- sum -= nums[i];
- i++;
- }
- }
- return result === Infinity ? 0 : result;
- }
这里引用一张代码随想录的滑窗图片。

根据子序列情况,通过不断调整起始位置 ,就是滑动窗口的要点
再看几道相关的题。
leetcode 76 最小覆盖子串
- var minWindow = function(s, t) {
-
- let left = 0 ,right = 0
- let substr = ''
- let res = ''
- let need = new Map()
- // 首先统计子串中的字母种类和数量 利用map统计
- for(let c of t){
- need.set(c,need.has(c)?need.get(c)+1:1)
- }
- // 需要的字母种类 就是map的长度
- let needType = need.size
- while(right < s.length){
- const c = s[right]
- // 在窗口扩大时 如果有子串需要的字母 就减少需要的量
- if(need.has(c)){
- need.set(c,need.get(c)-1)
- if(need.get(c)===0){
- needType-=1
- }
- }
- // 当子串需求都满足了 缩小窗口
- while(needType===0){
- //
- const c2 = s[left]
- substr = s.substring(left,right+1)
- if(!res||substr.length
length){ - res = substr
- }
- // 缩小窗口时如果是需要的字母 那么再增加需要的字母数量
- if(need.has(c2)){
- need.set(c2,need.get(c2)+1)
- if(need.get(c2)){
- needType+=1
- }
- }
- left++
- }
- right++
- }
- return res
-
- };
leetcode 904 水果成篮
- var totalFruit = function(fruits) {
- let left = 0
- let right = 0
- let maxType = 2
- let fruitType = 0
- let fruitCnt = new Map()
- let maxLen
- while(right < fruits.length){
- const f = fruits[right]
- //如果是没加入的水果种类 种类数量增加
- if(!fruitCnt.get(f)) {
- fruitType+=1
- }
- //每一种水果的数量统计
- fruitCnt.set(f,fruitCnt.has(f)?fruitCnt.get(f)+1:1)
- // 当水果种类大于2 即不满足条件
- while(fruitType > maxType){
-
- const f2 = fruits[left]
- //缩小窗口的过程中 如果有已有的水果 那么水果数量减一
- if(fruitCnt.has(f2)){
- fruitCnt.set(f2,fruitCnt.get(f2)-1)
- }
- //如果数量减少完了 那就让水果种类减1
- if(fruitCnt.get(f2)===0){
- fruitType-=1
- }
- left++
- }
- // 在窗口扩大时 计算最大数目
- maxLen = Math.max(maxLen,(right - left)+1)
- right++
- }
- return right
- };