Leetcode-JS解法-滑动窗口

什么是滑动窗口

滑动窗口的思想是:维护一个窗口,通过滑动窗口的方式在数组或字符串上移动。窗口通常由两个指针(左指针和右指针)定义,左指针指向窗口的起始位置,右指针指向窗口的结束位置。

  • 初始化左右指针left和right,左右指针之间的内容就是窗口
  • 定义一个变量result记录当前的滑动窗口的结果
  • 定义一个变量bestResult记录当前滑动窗口下的最优结果

窗口的左边界和右边界永远只能向右移动,而不能向左移动,这是为了保证滑动窗口的时间复杂度是 O(n)。如果左右边界向左移动的话,这叫做"回溯",算法的时间复杂度就可能不止 O(n)

其实双指针和滑动窗口是有些许区别的。滑动窗口一句话就是右指针先出发,左指针视情况追赶右指针

模板代码

寻找最长滑窗

scss 复制代码
初始化left,right,result,bestResult
while(右指针没有到结尾){
  窗口扩大,加入right对应元素,更新当前result
	while(result不满足要求){
     窗口缩小,移除left对应元素,left右移
  }
	更新最优结果bestResult(注意是在外面更新)
  right++
}
返回bestResult

寻找最短滑窗

scss 复制代码
初始化left,right,result,bestResult
while(右指针没有到结尾){
  窗口扩大,加入right对应元素,更新当前result
	while(result不满足要求){
    更新最优结果bestResult(在while内更新)
    窗口缩小,移除left对应元素,left右移
  }
  right++
}
返回bestResult

相关题目

简-和为s的连续正数序列-剑指o57

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:

滑动窗口只有 右边界向右移动(扩大窗口)左边界向右移动(缩小窗口)

  • 当窗口的和<target的时候,窗口的和需要增加,右边界向右移动
  • 当窗口的和>target的时候,窗口的和需要减少,左边界向右移动
  • 当窗口的和=target的时候,记录当前结果[i,j),接下来找i+1开头的序列,窗口的左边界向右移动
javascript 复制代码
/**
 * @param {number} target
 * @return {number[][]}
 */
var findContinuousSequence = function(target) {
    // 这个窗口是一个左闭右开的区间
    let i=1//左边界
    let j=1//右边界
    let sum=0 //滑动窗口的数字和
    const res=[]
    // 只循环到目标值的一半,超过一半的话,加起来会超过target
    while(i<=Math.floor(target/2)){
        // 右边界移动
        if(sum<target){
            sum+=j
            j++
        }
        // 左边界移动
        else if(sum>target){
            sum-=i
            i++
        }
        // 记录结果
        else{
            let arr=[]
            for(let k=i;k<j;k++){
                arr.push(k)
            }
            res.push(arr)
            // 左边界移动
            sum-=i
            i++
        }
    }
    return res
};

中-长度最小的子数组-209

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:本质上是找最小滑窗

  • 定义左右指针,指向滑动窗口的开始和结束位置
  • 从右移动右指针,计算窗口内数之和sum
  • 如果sum>=target,就更新子数组最小长度(right-left+1)
  • 向右移动left,同时sum-left的值,直到sum<target
scss 复制代码
var minSubArrayLen = function (target, nums) {
   let left = right = sum = bestResult = 0;
  //右指针到最右边结束循环
    while(right<nums.lenght){
      sum=sum+nums[right]//记录当前滑动窗口的值
      // 这里记住是要用while,而不是if!!
      // 满足条件
      while(sum>=target){
         // 如果当前的最好的结果大于左右指针之间的长度,更新最优结果
        if(right-left+1<bestResult||bestResult==0){
           bestResult = right - left + 1;
        }
        // 将左指针向右移,并且左指针的值从当前结果里面减掉
        sum -= nums[left];
        left++;
      }
      right++; // 向右滑动窗口
    }
    return bestResult;
}

中-无重复字符的最长字符串-3

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:本质是找最长滑窗

  • 创建一个set
  • 两个指针(left和right)
  • 如果set里面没有s[right],说明目前为止还没有重复的字符,把s[right]添加到set里,然后更新最大不重复字符的数量
  • 如果set里有s[right],则从set里开始删除s[left],并且递增,再检查set是否有s[right],循环直到没有s[right]为止
  • 重复步骤3,4,直到遍历完整个字符串

使用set

scss 复制代码
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
     if(s.length === 0){
        return 0
    }
    const set = new Set()
    let left=0,right=0,maxLength=0
    while(right<s.length){
        if(!set.has(s[right])){
            set.add(s[right])
          	right++
            // 更新最大长度
            maxLength=Math.max(maxLength,set.size)
        }else{
            // 存在重复值,则删除set中s[i]以及之前的元素
            while(set.has(s[right])){
                set.delete(s[left])
                left++
            }
            set.add(s[right])
          	right++
        }
    }
    return maxLength
};

不使用set

这种写法更容易理解,但是需要考虑重复的情况

scss 复制代码
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let left=right=len=maxLen=0
    let arr=[]//记录当前滑动窗口包含的字符
    //当右指针指向字符串最右边时停止循环
    while(right<s.length){
        //如果窗口中的字符串包含了右指针指向的元素
        if(arr.includes(s[right])){
            // 当arr中包含右指针指向的字符,删除该元素
            while(arr.includes(s[right])){
                arr.shift()//删除
                len--//更新当前的长度
                left++//左指针右移动
            }
            //循环结束后,arr中不包含右指针指向的元素
            arr.push(s[right])
            len++
            right++
        }else{
            arr.push(s[right])
            len++
            //如果当前长度大于最大长度
            if(len>maxLen){
                maxLen=len
            }
            right++
        }
    }
    return maxLen
};

中-水果成篮-904

力扣

题目简述:有两个篮子,每个篮子只能放一种水果,找出储存最多水果的方案,本质是找最大滑窗

思路:

Leetcode刷题 904. 水果成篮Fruit Into Baskets_哔哩哔哩_bilibili

  • 定义两个变量,right为窗口的右边界,left为窗口的左边界
  • 使用map储存每个水果最后出现的位置
  • 窗口中只能放两个水果,当出现第三种水果时,将第一种水果去除出去,即移动j的坐标
scss 复制代码
/**
 * @param {number[]} fruits
 * @return {number}
 */
var totalFruit = function(fruits) {
  const map = new Map()
  let max = 1
  let left=0
  for(let right=0;right<tree.length;right++){
    // map用来存储每个水果出现的最后位置
    map.set((tree[i],i))
    // 不满足条件(超过两种水果)
    if(map.size>2){
      // 初始值(给一个最大的值)
      let minIndex = tree.length-1
      // 找到map中比较小的坐标
      for(const [fruit,index] of map){
        if(index<minIndex){
          minIndex = index
        }
      }
      // 删除小坐标的水果
      map.delete(tree[minIndex])
      // j往后移动
      left=minIndex+1
    }
    // 更新最大值
    max = Math.max(max,right-left+1)
  } 
  return max 
};
相关推荐
GIS程序媛—椰子22 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00128 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端31 分钟前
Content Security Policy (CSP)
前端·javascript·面试
乐闻x34 分钟前
ESLint 使用教程(一):从零配置 ESLint
javascript·eslint
木舟100935 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤43911 小时前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
为什么这亚子1 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
1 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习
~yY…s<#>1 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js