[leetcode]打开你的心灵视界———滑动窗口

250 阅读2分钟

239. 滑动窗口最大值

思路与代码实现

利用双端队列, 在其头部存储当前窗口的最大值的索引

向右滑动窗口, 从尾部入队列的元素从尾部向前依次比较, 从尾部弹出比其小的元素, 再将其从尾部入队

如果双端队列的头部元素超出窗口的宽度, 则从头部删除该元素

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
  // 考虑边界情况
  if (nums.length < k || nums.length === 0) {
    return nums
  } else {
    const numsLength = nums.length
    const answer = []
    const slidingWindow = []
    // 初始化第一个窗口
    for (let i = 0; i < k; ++i) {
      while (slidingWindow.length > 0 && nums[i] > nums[slidingWindow[slidingWindow.length - 1]]) {
        slidingWindow.pop()
      }
      slidingWindow.push(i)
    }
    answer.push(nums[slidingWindow[0]])
    // 遍历数组
    for (let i = k; i < numsLength; ++i) {
      // 删除超出窗口左侧的数组下标
      let start = 0
      while (slidingWindow[start] < i - k + 1) {
        ++start
      }
      slidingWindow.splice(0, start)
      // 删除小于当前元素值的数组下标
      while (slidingWindow.length > 0 && nums[i] > nums[slidingWindow[slidingWindow.length - 1]]) {
        slidingWindow.pop()
      }
      slidingWindow.push(i)
      answer.push(nums[slidingWindow[0]])
    }
    return answer
  }
}

76. 最小覆盖子串

思路与代码实现

向右移动右指针, 直至符合条件, 再向右移动左指针, 直至不符合条件

/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
var minWindow = function(s, t) {
  if (s.length < t.length) {
    return ''
  } else {
    let leftPointer = 0
    let rightPointer = 0
    let res = s
    let existFlag = false
    const targetHashTable = {}
    const windowHashTable = {}
    // 生成目标哈希表
    for (let i = t.length - 1; i >= 0; i--) {
      if (targetHashTable[t[i]] === undefined) {
        targetHashTable[t[i]] = 1
      } else {
        targetHashTable[t[i]]++
      }
    }
    // 是否符合要求
    function isFit (windowHashTable, targetHashTable) {
      for (let str in targetHashTable) {
        if (windowHashTable[str] === undefined) {
          return false
        } else {
          if (windowHashTable[str] < targetHashTable[str]) {
            return false
          }
        }
      }
      existFlag = true
      return true
    }
    while (rightPointer < s.length) {
      // 向右移动右指针, 并在窗口哈希表记录每个字符出现的次数
      const currentStr = s[rightPointer]
      if (windowHashTable[currentStr] === undefined) {
        windowHashTable[currentStr] = 1
      } else {
        windowHashTable[currentStr]++
      }
      rightPointer++
      // 向右移动左指针, 缩小窗口, 直至不符合要求
      while (isFit(windowHashTable, targetHashTable)) {
        // 更新结果
        const _res = s.substring(leftPointer, rightPointer)
        if (_res.length < res.length) {
          res = _res
        }
        const currentStr = s[leftPointer]
        windowHashTable[currentStr]--
        leftPointer++
      }
    }
    return existFlag ? res : ''
  }
};

209. 长度最小的子数组

思路与代码实现

同上, 向右移动右指针, 直至符合条件, 再向右移动左指针, 直至不符合条件

/**
 * @param {number} s
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function (s, nums) {
  let sum = 0
  let leftPointer = 0
  let rightPointer = 0
  let res = nums.length
  let flag = false
  while (rightPointer < nums.length && sum < s) {
    // 向右移动右指针, 增加窗口宽度
    sum += nums[rightPointer]
    rightPointer++
    // 向右移动左指针, 减小窗口宽度
    while (sum >= s) {
      if (sum >= s && rightPointer - leftPointer <= res) {
        flag = true
        res = rightPointer - leftPointer
      }
      sum -= nums[leftPointer]
      leftPointer++
    }
  }
  return flag ? res : 0
};