
The Sliding Window pattern is one of the most powerful techniques in algorithmic problem-solving.
It transforms problems that would normally require O(n²) brute-force solutions into clean, efficient O(n) approaches by maintaining a “window”
over a portion of the data and adjusting it as you process the input.
At its core, a sliding window lets you examine contiguous segments of an array or string without recomputing information from scratch every time.
Instead of restarting from zero for each possible subarray or substring, you reuse previous computations and update the window incrementally.
Sliding window is ideal when the problem involves:
If the problem involves checking or optimizing a continuous segment of a sequence, sliding window is often the correct approach. Look for these signals in the problem statement:
If the word subarray or substring appears—and the subarray must be contiguous—this is an immediate hint.
Usually, a naïve brute-force approach for the problems above checks every possible subarray:
for L in range(n):
for R in range(L, n):
check subarray nums[L:R]
This leads to:
Sliding window avoids recomputing work.
Instead, it grows or shrinks the window by one step at a time, preserving most of the previously computed information.
This transformation often reduces complexity to .
Sliding window is not just an optimization—it is a paradigm shift in how you iterate over collections.
Once you recognize the structure of these problems, the sliding window template becomes your go-to tool for a whole class of subarray and substring challenges.
The Sliding Window technique is closely related to the Two Pointers pattern, and many problems can be solved with either approach depending on how you structure the logic.
However, the two patterns serve different purposes, and understanding their conceptual differences will help you choose the right one for each problem.
Main features of the Two Pointers techniques are:
Instead Sliding Window as we saw before:
R expands the windowL shrinks the windowSo, in short: Every sliding window uses two pointers, but not every two-pointer technique is a sliding window.
The Sliding Window pattern appears in two main forms: Fixed-Size and Dynamic-Size.
Both use two pointers to represent a window over a contiguous region of the input, but their behavior and use cases differ significantly.
A Fixed-Size Sliding Window maintains a window of a constant length k.
You slide this window across the array or string while keeping track of some running information (sum, frequency, number of distinct elements, etc.).
Usually, it works in this way:
k.The window size never changes.
You are likely dealing with a fixed-size window if:
k is given explicitlykkThis is a general template for a Fixed-Size Sliding Window in TypeScript:
function fixedWindow(nums: number[], k: number): number {
let windowSum = 0;
// Build the first window
for (let i = 0; i < k; i++) {
windowSum += nums[i];
}
let result = windowSum;
// Slide the window
for (let r = k; r < nums.length; r++) {
windowSum += nums[r] - nums[r - k]; // add right, remove left
result = Math.max(result, windowSum); // example operation
}
return result;
}
A Dynamic-Size Sliding Window adjusts its boundaries based on constraints. The window can grow or shrink depending on whether it currently satisfies a condition (e.g., at most one duplicate, sum ≥ target, no more than k replacements).
This is how it typically works:
The window size is not fixed, it reacts to the rules of the problem.
You are likely dealing with a dynamic-size window if:
This is a general template for a Dynamic-Size Sliding Window in TypeScript:
function dynamicWindow(s: string): number {
let L = 0;
const freq = new Map<string, number>();
let best = 0;
for (let R = 0; R < s.length; R++) {
const char = s[R];
freq.set(char, (freq.get(char) ?? 0) + 1);
// Shrink while the window is invalid
while (windowIsInvalid(freq)) {
const leftChar = s[L];
freq.set(leftChar, freq.get(leftChar)! - 1);
L++;
}
// Update result when window is valid
best = Math.max(best, R - L + 1);
}
return best;
}
function windowIsInvalid(freq: Map<string, number>): boolean {
// Placeholder for the specific problem's rule
return < place your cindition here >;
}
Below you can find a summary table of time and space complexities for the sliding window pattern, distinguishing between fixed-size and dynamic windows.
| Window Type / Operation | Time Complexity | Space Complexity |
|---|---|---|
| Fixed-size sliding window | O(n) | O(1) or O(k) |
| Dynamic-size sliding window | O(n) | O(1) to O(n) depending on frequency maps |
| Expand step (right pointer) | O(1) per step | — |
| Shrink step (left pointer) | O(1) per step | — |
| Full traversal (expand + shrink) | O(n) — each index visited at most twice | — |
The sliding window technique typically runs in O(n) time because:
| Problem | Technique | Solution |
|---|---|---|
| Maximum Average Subarray I | Fixed Sliding Window | Solution |
| Find All Anagrams in a String | Fixed Sliding Window | Solution |
| Permutation in String | Fixed Sliding Window | Solution |
| Maximum Sum of Distinct Subarrays With Length K | Fixed Sliding Window | Solution |
| Substring with Concatenation of All Words | Fixed Sliding Window | Solution |
| Longest Substring Without Repeating Characters | Dynamic Sliding Window | Solution |
| Longest Repeating Character Replacement | Dynamic Sliding Window | Solution |
| Minimum Size Subarray Sum | Dynamic Sliding Window | Solution |
| Max Consecutive Ones III | Dynamic Sliding Window | Solution |
| Minimum Window Substring | Dynamic Sliding Window | Solution |