Techniques & Patterns
Keep a moving window over the data instead of recomputing from scratch.
When a problem asks about a contiguous chunk of an array/string (a 'window'), sliding it forward — adding what enters, removing what leaves — avoids recomputing the whole thing at every position. Fixed-size windows (e.g. 'sum of every 3 consecutive items') and variable-size windows (e.g. 'smallest chunk that sums to at least X') both use this, turning what looks like O(n·k) into O(n).
// Sliding Window — keep a "window" (a start and end index) over part of
// an array/string and slide it forward, instead of recomputing from
// scratch for every possible position. Turns O(n * k) into O(n).
// --- Fixed-size window: max sum of any k consecutive elements ---
function maxSumFixedWindow(nums, k) {
let windowSum = 0;
for (let i = 0; i < k; i++) windowSum += nums[i]; // build the first window
let maxSum = windowSum;
for (let i = k; i < nums.length; i++) {
windowSum += nums[i] - nums[i - k]; // slide: add new, drop oldest
maxSum = Math.max(maxSum, windowSum);
}
return maxSum;
}
// --- Variable-size window: smallest subarray with sum >= target ---
function smallestSubarrayWithSum(nums, target) {
let left = 0;
let sum = 0;
let minLength = Infinity;
for (let right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= target) {
minLength = Math.min(minLength, right - left + 1);
sum -= nums[left]; // shrink from the left
left++;
}
}
return minLength === Infinity ? 0 : minLength;
}
// --- Variable-size window: longest substring without repeating characters ---
function longestUniqueSubstring(str) {
const seen = new Map(); // char -> last index seen
let left = 0;
let maxLength = 0;
for (let right = 0; right < str.length; right++) {
const char = str[right];
if (seen.has(char) && seen.get(char) >= left) {
left = seen.get(char) + 1; // jump window start past the repeat
}
seen.set(char, right);
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
// --- Example Usage ---
console.log("maxSumFixedWindow([2,1,5,1,3,2], 3):", maxSumFixedWindow([2, 1, 5, 1, 3, 2], 3)); // 9
console.log(
"smallestSubarrayWithSum([2,3,1,2,4,3], 7):",
smallestSubarrayWithSum([2, 3, 1, 2, 4, 3], 7),
); // 2 ([4,3])
console.log("longestUniqueSubstring('abcabcbb'):", longestUniqueSubstring("abcabcbb")); // 3 ("abc")
// Real uses of this exact idea:
// - rate limiting ("max N requests per rolling 60s window")
// - rolling averages / moving averages in dashboards and charts
// - "typing search box" style debounced-lookahead over recent input