滑动窗口是解决数组或列表问题时的常见算法模式,特别是涉及到子串或连续序列的问题。这种方法可以帮助我们在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;
}
常见题型
- 最大/最小的连续子数组:如上面的模板示例。
- 字符串的所有字母异位词:找到字符串中所有字母异位词的起始索引。
- 最长无重复字符的子串:找到不含有重复字符的最长子串的长度。
- 含有K个不同字符的最短字符串:找到含有K个不同字符的最短子串。
- 最小覆盖子串:找到包含所有指定字符的最小子串。
- 长度最小的子数组:找到和大于或等于给定值的最小连续子数组的长度。
滑动窗口技巧在实际问题中是非常实用的,特别是涉及到子串或子数组的连续问题。这种技巧可以帮助我们减少不必要的循环,从而提高算法的效率。
/**
* 返回数组中长度为 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个数字的和)