《JavaScript算法》滑动窗口

67 阅读2分钟

滑动窗口是解决数组或列表问题时的常见算法模式,特别是涉及到子串或连续序列的问题。这种方法可以帮助我们在O(n)时间复杂度内解决一些可能需要O(n^2)复杂度的问题。

function maxSlidingWindow(nums, k) {
    let result = [];
    let window = []; // 用于存储数字的索引

    for (let i = 0; i < nums.length; i++) {
        // 移除滑动窗口左侧超出范围的元素
        if (window.length && window[0] <= i - k) {
            window.shift();
        }

        // 从后向前移除小于当前元素的元素,确保window[0]是当前滑动窗口中的最大值
        while (window.length && nums[window[window.length - 1]] < nums[i]) {
            window.pop();
        }

        // 添加当前元素索引
        window.push(i);

        // i + 1 >= k时,确保滑动窗口的大小为k
        if (i + 1 >= k) {
            result.push(nums[window[0]]);
        }
    }

    return result;
}

常见题型

  1. 最大/最小的连续子数组:如上面的模板示例。
  2. 字符串的所有字母异位词:找到字符串中所有字母异位词的起始索引。
  3. 最长无重复字符的子串:找到不含有重复字符的最长子串的长度。
  4. 含有K个不同字符的最短字符串:找到含有K个不同字符的最短子串。
  5. 最小覆盖子串:找到包含所有指定字符的最小子串。
  6. 长度最小的子数组:找到和大于或等于给定值的最小连续子数组的长度。

滑动窗口技巧在实际问题中是非常实用的,特别是涉及到子串或子数组的连续问题。这种技巧可以帮助我们减少不必要的循环,从而提高算法的效率。

/**
 * 返回数组中长度为 k 的最大子数组和。
 * 使用滑动窗口技术解决问题。
 * 
 * @param {number[]} nums - 输入的数组。
 * @param {number} k - 子数组的长度。
 * @returns {number} 最大子数组和。
 */
function maxSubArraySum(nums, k) {
  if (nums.length < k) return null;

  // 初始化窗口的和以及最大的和。
  let windowSum = 0;
  for (let i = 0; i < k; i++) {
    windowSum += nums[i];
  }
  let maxSum = windowSum;

  // 使用滑动窗口技术移动窗口,并更新 maxSum。
  for (let i = k; i < nums.length; i++) {
    // 新的窗口和 = 旧的窗口和 - 最左侧的元素 + 新的元素。
    windowSum = windowSum - nums[i - k] + nums[i];
    maxSum = Math.max(maxSum, windowSum);
  }

  return maxSum;
}

// 测试
const arr = [2, 3, 4, 1, 5];
const k = 2;
console.log(maxSubArraySum(arr, k)); // 输出:9 (因为 4 + 5 = 9 是最大的连续2个数字的和)