算法中的滑动窗口问题

99 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

什么情况下使用滑动窗口?

  • 当有确定大小的子序列时,可以通过滑动窗口将嵌套的循环问题,转成单次循环,降低时间复杂度
  • TCP中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据

例题

存在重复元素 II

  • 滑动窗口大小为K,创建一个set,大于K时删除左边第一个元素,这样不断移动,直到数组遍历完成
var containsNearbyDuplicate = function(nums, k) {
  let set = new Set()
  for (let i = 0; i < nums.length; i++) {
      if (set.has(nums[i])) {
          return true
      }
      set.add(nums[i])
      set.size > k && set.delete(nums[i-k])
  }
  return false
};

image.png

找到字符串中所有字母异位词

  • 我们可以设置两个指针记录滑动窗口,通过右指针向右移,扩充滑动窗口,当窗口大小大于p的长度时,左指针右移,缩减滑动窗口,如此不断向右滑动
  • 设置pMap,用来存储p字符串中每个字符出现的个数,设置sMap,用来记录滑动窗口中每个字符出现的个数
  • 设置count,用来记录滑动窗口中和p字符串中相同个数的字符种类
  • 当出界时,如果相同个数的字符种类相同且长度相同,则记录一个异位词结果
var findAnagrams = function(s, p) {
  if (!s.length || !p.length) {
      return []
  }
  let pMap = {}
  for (let i = 0; i < p.length; i++) {
      let a = p[i]
      pMap[a] = (pMap[a] || 0) + 1
  }
  let l = r = 0
  let res = []
  let sMap = {}
  let count = 0
  while (r < s.length) {
    let b = s[r]
    r++
    if (pMap[b]) {
        sMap[b] = (sMap[b] || 0) + 1
        if (sMap[b] === pMap[b]) {
            count++
        }
    }
    if (r - l >= p.length) {
        if (count === Object.keys(pMap).length) {
            res.push(l)
        }
        let c = s[l]
        l++
        if (pMap[c]) {
           if (sMap[c] === pMap[c]) {
               count--
           }
           sMap[c]--
        }
    }
  }
  return res
};

image.png

时间复杂度为O(n),即为s的长度,空间复杂度为O(K),即为pMap的大小