秋招必会的算法技巧--滑动窗口

6 篇文章 0 订阅
2 篇文章 0 订阅

所谓的滑动窗口,就是通过不断调整指针的起始位置和终止位置,从而得出想要的结果,我们先来看一道算法题

Leetcode 209 长度最小的子数组 

 力扣题目链接

   给定一个含有 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<res.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
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值