Sliding Window Templates - TypeScript
Template 1: Fixed Size Window
When to use: Problems asking for results in windows of exact size K
typescript
/**
* Fixed Size Sliding Window Template
* Time: O(n), Space: O(1) to O(k)
*/
function fixedSizeWindow<T>(nums: T[], k: number): any {
if (nums.length < k) return null;
let left = 0;
let windowState = initializeWindowState(); // depends on problem
for (let right = 0; right < nums.length; right++) {
// 1. EXPAND: Add current element to window
addToWindow(windowState, nums[right]);
// 2. CHECK: If window size < k, continue expanding
if (right - left + 1 < k) {
continue;
}
// 3. PROCESS: Window has exactly k elements
if (right - left + 1 === k) {
// Process current window and update result
processWindow(windowState);
// 4. SHRINK: Remove leftmost element
removeFromWindow(windowState, nums[left]);
left++;
}
}
return result;
}Template 2: Dynamic Window - Maximum Length
When to use: Finding longest subarray/substring satisfying a condition
typescript
/**
* Dynamic Sliding Window - Maximum Length
* Time: O(n), Space: O(1) to O(n)
*/
function longestWindow<T>(nums: T[], isValidCondition: () => boolean): number {
let left = 0;
let maxLength = 0;
let windowState = initializeWindowState();
for (let right = 0; right < nums.length; right++) {
// 1. EXPAND: Always add right element to window
addToWindow(windowState, nums[right]);
// 2. SHRINK: While condition is violated, shrink from left
while (!isValidCondition()) {
removeFromWindow(windowState, nums[left]);
left++;
}
// 3. UPDATE: Current window is valid, update max length
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}Template 3: Dynamic Window - Minimum Length
When to use: Finding shortest subarray/substring satisfying a condition
typescript
/**
* Dynamic Sliding Window - Minimum Length
* Time: O(n), Space: O(1) to O(n)
*/
function shortestWindow<T>(nums: T[], isValidCondition: () => boolean): number {
let left = 0;
let minLength = Infinity;
let windowState = initializeWindowState();
for (let right = 0; right < nums.length; right++) {
// 1. EXPAND: Always add right element to window
addToWindow(windowState, nums[right]);
// 2. SHRINK: While condition is satisfied, try to minimize window
while (isValidCondition()) {
// Update result: current window is valid and potentially shorter
minLength = Math.min(minLength, right - left + 1);
// Try to shrink window from left
removeFromWindow(windowState, nums[left]);
left++;
}
}
return minLength === Infinity ? 0 : minLength;
}Template 4: Sliding Window with HashMap
When to use: Problems involving character/element frequencies
typescript
/**
* Sliding Window with HashMap Template
* Time: O(n), Space: O(k) where k is size of character set
*/
function slidingWindowWithMap<T>(nums: T[]): any {
let left = 0;
let result = initializeResult();
const frequency = new Map<T, number>();
for (let right = 0; right < nums.length; right++) {
// 1. EXPAND: Add right element to frequency map
const rightElement = nums[right];
frequency.set(rightElement, (frequency.get(rightElement) || 0) + 1);
// 2. ADJUST: Shrink window based on condition
while (shouldShrinkWindow(frequency)) {
const leftElement = nums[left];
const leftCount = frequency.get(leftElement) || 0;
if (leftCount === 1) {
frequency.delete(leftElement);
} else {
frequency.set(leftElement, leftCount - 1);
}
left++;
}
// 3. UPDATE: Process current valid window
updateResult(result, frequency, right - left + 1);
}
return result;
}Template 5: Sliding Window with Two Pointers
When to use: Array problems with specific sum or difference constraints
typescript
/**
* Sliding Window with Two Pointers
* Time: O(n), Space: O(1)
*/
function twoPointerWindow(nums: number[], target: number): any {
let left = 0;
let right = 0;
let result = initializeResult();
let windowSum = 0;
while (right < nums.length) {
// 1. EXPAND: Add right element
windowSum += nums[right];
// 2. SHRINK: Adjust left pointer based on condition
while (windowSum >= target && left <= right) {
updateResult(result, left, right);
windowSum -= nums[left];
left++;
}
right++;
}
return result;
}Template 6: Sliding Window for Substring Problems
When to use: String pattern matching with complex constraints
typescript
/**
* Sliding Window for Substring Problems
* Time: O(n + m), Space: O(m) where m is pattern length
*/
function substringWindow(s: string, pattern: string): any {
if (s.length < pattern.length) return null;
const patternFreq = buildFrequencyMap(pattern);
const windowFreq = new Map<string, number>();
let left = 0;
let validChars = 0;
let result = initializeResult();
for (let right = 0; right < s.length; right++) {
// 1. EXPAND: Add right character
const rightChar = s[right];
windowFreq.set(rightChar, (windowFreq.get(rightChar) || 0) + 1);
// Update valid character count
if (patternFreq.has(rightChar) && windowFreq.get(rightChar) === patternFreq.get(rightChar)) {
validChars++;
}
// 2. SHRINK: While window contains all pattern characters
while (validChars === patternFreq.size) {
updateResult(result, left, right);
const leftChar = s[left];
const leftCount = windowFreq.get(leftChar) || 0;
if (leftCount === 1) {
windowFreq.delete(leftChar);
} else {
windowFreq.set(leftChar, leftCount - 1);
}
// Update valid character count
if (
patternFreq.has(leftChar) &&
(windowFreq.get(leftChar) || 0) < patternFreq.get(leftChar)!
) {
validChars--;
}
left++;
}
}
return result;
}Helper Functions
typescript
// Generic helper functions to implement based on specific problem
function initializeWindowState(): any {
// Initialize based on problem requirements
return {};
}
function addToWindow(state: any, element: any): void {
// Add element to window state
}
function removeFromWindow(state: any, element: any): void {
// Remove element from window state
}
function processWindow(state: any): void {
// Process current window
}
function initializeResult(): any {
// Initialize result based on problem
return null;
}
function updateResult(result: any, ...args: any[]): void {
// Update result based on current window
}
function shouldShrinkWindow(frequency: Map<any, number>): boolean {
// Determine if window should be shrunk
return false;
}
function buildFrequencyMap(str: string): Map<string, number> {
const freq = new Map<string, number>();
for (const char of str) {
freq.set(char, (freq.get(char) || 0) + 1);
}
return freq;
}Template Selection Guide
| Problem Type | Template | Key Characteristics |
|---|---|---|
| Fixed window size K | Template 1 | Window size never changes |
| Longest valid substring | Template 2 | Expand greedily, shrink when invalid |
| Shortest valid substring | Template 3 | Shrink while valid |
| Character frequency problems | Template 4 | Uses HashMap for counting |
| Sum/difference constraints | Template 5 | Two pointers with numeric conditions |
| Pattern matching | Template 6 | Complex string constraints |