Introduction to Sliding Window Pattern
Introduction to the Sliding Window Pattern
The sliding window pattern turns a naive nested-loop scan over a contiguous run of array or string elements into a single pass. Instead of recomputing an aggregate (sum, max, count of distinct characters, etc.) from scratch for every candidate window, you maintain the aggregate incrementally as a window's boundaries move: extend the window by including one new element on the right, and, when the window needs to shrink, retract it by excluding one element on the left. Both operations touch each array element a bounded number of times, which is what collapses the complexity from quadratic to linear.
This page covers two shapes of the pattern (fixed-size and variable-size windows), derives the complexity of each, and, critically, tells you when sliding window is the wrong tool, because two other named techniques — prefix sums and a monotonic deque — solve overlapping problems better under specific conditions.
Recognize the pattern
Reach for sliding window when a problem asks for something about every contiguous subarray or substring of an array or string, and the tracked quantity can be updated incrementally as the window moves by one position. Typical tells:
- "Find the maximum or average sum of any subarray of size K" (fixed-size window).
- "Find the smallest subarray whose sum is at least S" (variable-size window).
- "Find the longest substring with at most K distinct characters" (variable-size window).
- The words contiguous, substring, or subarray appear, as opposed to subsequence, which permits skipping elements. A subsequence has no two adjacent boundaries you can slide, so it generally needs dynamic programming instead.
Sliding window is one of several named techniques competing for this same problem space. The next section places it explicitly against the two closest alternatives so you know when to reach for something else.
When to use it, and when not to
Alternative 1: prefix-sum array
For the exact example used on this page — the sum of every window of size K in a fixed array — a prefix-sum array is the natural competing technique. Precompute prefix[i] = arr[0] + ... + arr[i-1] once in O(N) time and O(N) space; then the sum of any window [l, r] is prefix[r+1] - prefix[l] in O(1), for any l and r, not just ones reachable by sliding one step at a time.
- Use prefix sums instead when you need sums of arbitrary, non-adjacent ranges queried repeatedly (e.g. "answer Q range-sum queries after this array is built"), or when windows are not visited in left-to-right sliding order.
- Use sliding window instead when you only ever need windows visited in order with boundaries moving monotonically, and you want O(1) extra space rather than an O(N) auxiliary array — the running-sum technique below achieves the same O(N) total time as prefix sums without materializing the prefix array.
Alternative 2: monotonic deque
Sliding window's incremental-update trick works cleanly for aggregates that are cheaply reversible under removal — sum, count, XOR. It breaks down for max or min: when the element leaving the window on the left was the window's maximum, you cannot recover the new maximum from the old one in O(1); naively you'd rescan the window, which is back to O(N*K).
- Use a monotonic deque instead for "maximum/minimum of every window of size K." The deque stores indices in decreasing (or increasing) order of value, evicting from the back any index whose value is dominated by the newly-added element, and evicting from the front any index that has fallen outside the window. This keeps the window's max/min accessible in O(1) with amortized O(1) work per step, still O(N) overall.
- Use plain sliding window instead when the aggregate is sum/count/product (no undominated-element problem), where a simple running variable suffices and a deque is unneeded machinery.
When NOT to use sliding window at all
The variable-size "shrink while the condition holds" version of sliding window (e.g. "smallest subarray with sum at least S") relies on a monotonicity assumption: growing the window can only move the aggregate in one direction, and shrinking it can only move it back. This holds for sums only when all elements are non-negative. The moment the array can contain a negative number, growing the window can decrease the sum, so the two-pointer shrink logic silently gives wrong answers — it stops shrinking at points that no longer bound a valid answer, and can skip over the true minimal window entirely, with no exception or crash to signal the bug.
The worked example on this page — [2, 1, 5, 1, 3, -1, 4, 2] — contains a -1. That's fine for the fixed-size window sum in the first worked example below (fixed windows don't rely on monotonicity; every window is exactly K elements regardless of sign). But it means this same array is not safe input for the variable-size "shrink on threshold" technique later on this page without switching to a different approach (prefix sums plus binary search, or a deque-based technique) once negatives are possible. The variable-size example below is deliberately run on a non-negative array to keep the pattern valid, and that restriction is called out again at that point.
Fixed-size window: brute force vs. incremental — and the trade-off that matters more
Problem: given arr = [2, 1, 5, 1, 3, -1, 4, 2] and K = 5, find the maximum sum of any contiguous subarray of size K.
Brute force
For each of the N - K + 1 starting positions, sum K elements from scratch: (N - K + 1) * K additions in the worst case, which is O(N*K) time and O(1) extra space (or O(N) if you store every window's sum, not just the max).
Optimized: running sum
Compute the sum of the first window once (O(K) work, done exactly one time). Then slide: to move from window [i, i+K-1] to [i+1, i+K], add arr[i+K] and subtract arr[i] — two operations, regardless of K. Track a running max as you go.
Complexity derivation: the first window costs O(K). Each of the remaining N - K slides costs O(1). Total work is O(K) + O(N - K) * O(1) = O(K) + O(N - K) = O(N). Extra space is O(1): one running-sum variable and one running-max variable, no auxiliary array (O(N) only if every window's sum must be retained, e.g. to return the full list rather than just the max).
This before/after comparison (O(N*K) brute force vs. O(N) running sum) is a within-technique optimization — both versions are sliding window, one just recomputes and the other reuses. It is not a trade-off against a different named algorithm. The real cross-technique trade-offs are against prefix sums and the monotonic deque, covered above: pick sliding window over those when you have a simple reversible aggregate (sum/count) visited in strictly left-to-right window order and you want O(1) extra space; pick prefix sums when you need random-access range queries; pick a monotonic deque when the aggregate is max/min.
Traced worked example: fixed-size window (K = 5)
arr = [2, 1, 5, 1, 3, -1, 4, 2], N = 8, K = 5, so there are N - K + 1 = 4 windows.
| Step | Window (indices) | Operation | Sum | Max so far |
|---|---|---|---|---|
| Init | [0..4] = 2,1,5,1,3 | sum of first K elements | 12 | 12 |
| Slide 1 | [1..5] = 1,5,1,3,-1 | 12 - arr[0] + arr[5] = 12 - 2 + (-1) | 9 | 12 |
| Slide 2 | [2..6] = 5,1,3,-1,4 | 9 - arr[1] + arr[6] = 9 - 1 + 4 | 12 | 12 |
| Slide 3 | [3..7] = 1,3,-1,4,2 | 12 - arr[2] + arr[7] = 12 - 5 + 2 | 9 | 12 |
Answer: maximum sum of any size-5 window is 12 (achieved by both window 1 and window 3). Total work: one O(K) initial sum plus three O(1) slides — O(N) overall, matching the derivation above, and no auxiliary array was needed beyond two scalars, so extra space is O(1).
Variable-size window: smallest subarray with sum at least S
This is the harder, more common interview shape flagged in "Recognize the pattern" above, and it works differently from the fixed-size case: the right pointer always advances, but the left pointer only advances (shrinks the window) while a condition continues to hold, rather than in lockstep with the right pointer.
Precondition: as established above, this technique requires all elements to be non-negative, so that growing the window never decreases the sum and shrinking it never increases the sum. Problem: given arr = [2, 1, 5, 1, 3, 2, 4, 2] (the earlier example's -1 replaced with a non-negative 2 specifically so this technique is valid) and S = 8, find the length of the smallest contiguous subarray with sum ≥ S.
Algorithm
- Maintain
left = 0,sum = 0,best = infinity. - For each
rightfrom 0 to N-1: addarr[right]tosum. - While
sum >= S: recordbest = min(best, right - left + 1), then subtractarr[left]fromsumand incrementleft(shrink as far as the condition allows). - Return
best(or 0/none if it never dropped below infinity).
Traced example (S = 8)
| right | arr[right] | sum | shrink? | best |
|---|---|---|---|---|
| 0 | 2 | 2 | no (2 < 8) | ∞ |
| 1 | 1 | 3 | no | ∞ |
| 2 | 5 | 8 | yes: len 3, drop arr[0]=2 -> sum 6, left=1 | 3 |
| 3 | 1 | 7 | no (7 < 8) | 3 |
| 4 | 3 | 10 | yes: len 4 (worse, skip); drop arr[1]=1 -> sum 9, left=2, len 3 (tie, skip); drop arr[2]=5 -> sum 4, left=3, stop (4<8) | 3 |
| 5 | 2 | 6 | no | 3 |
| 6 | 4 | 10 | yes: len 4 (skip); drop arr[3]=1 -> sum 9, left=4, len 3 (tie); drop arr[4]=3 -> sum 6, left=5, stop | 3 |
| 7 | 2 | 8 | yes: len 3 (tie); drop arr[5]=2 -> sum 6, left=6, stop | 3 |
Answer: smallest subarray with sum ≥ 8 has length 3 (the first window achieving length 3 was indices [0,2] = 2,1,5, sum 8). Complexity: the right pointer advances N times total; the left pointer also advances at most N times total across the whole run (it never resets backward), so total pointer movement is O(N), not O(N) per right-step — giving O(N) time overall, O(1) extra space.
Implementations
Fixed-size window (max sum of size-K subarray) — Java
public static int maxSumFixedWindow(int[] arr, int k) {
int n = arr.length;
if (n < k) throw new IllegalArgumentException("array shorter than window");
int windowSum = 0;
for (int i = 0; i < k; i++) windowSum += arr[i];
int best = windowSum;
for (int right = k; right < n; right++) {
windowSum += arr[right] - arr[right - k];
best = Math.max(best, windowSum);
}
return best;
}
Fixed-size window — Go
func maxSumFixedWindow(arr []int, k int) int {
n := len(arr)
if n < k {
panic("array shorter than window")
}
windowSum := 0
for i := 0; i < k; i++ {
windowSum += arr[i]
}
best := windowSum
for right := k; right < n; right++ {
windowSum += arr[right] - arr[right-k]
if windowSum > best {
best = windowSum
}
}
return best
}
Variable-size window (smallest subarray with sum ≥ S, non-negative elements only) — Java
public static int smallestSubarrayAtLeastS(int[] arr, int s) {
int left = 0, sum = 0, best = Integer.MAX_VALUE;
for (int right = 0; right < arr.length; right++) {
sum += arr[right];
while (sum >= s) {
best = Math.min(best, right - left + 1);
sum -= arr[left];
left++;
}
}
return best == Integer.MAX_VALUE ? 0 : best;
}
Variable-size window — Go
func smallestSubarrayAtLeastS(arr []int, s int) int {
left, sum, best := 0, 0, -1
for right := 0; right < len(arr); right++ {
sum += arr[right]
for sum >= s {
length := right - left + 1
if best == -1 || length < best {
best = length
}
sum -= arr[left]
left++
}
}
if best == -1 {
return 0
}
return best
}Sources
Compiled from: CLRS-style asymptotic analysis conventions for amortized two-pointer arguments; the classic "maximum sum subarray of size K" and "minimum size subarray sum" problem formulations as commonly presented in interview-preparation references (e.g. LeetCode #209 Minimum Size Subarray Sum); and the standard monotonic-deque construction for sliding-window maximum (LeetCode #239 Sliding Window Maximum) used here for the deque trade-off discussion.
L0 · Sliding window optimizes contiguous subarray search from O(n²) to O(n) by reusing the overlapping state of adjacent windows.
L1 · ⑤ Adversary/Edge — “We need to find the longest subarray with a sum equal to target, but the array contains negative numbers. Can we still use the standard sliding window?”
Trap: Yes, just expand if sum is less than target and shrink if it's greater.
Bar: With negative numbers, expanding can decrease the sum and shrinking can increase it, breaking the monotonicity invariant; use a prefix-sum hash map mapping prefix_sum -> index to resolve it in O(n) time and O(n) space. Two Pointers
L2 · ② Failure — “What happens if the input array is empty or smaller than the fixed window size K?”
Trap: The loop will naturally terminate or return null.
Bar: A naive initialization loop will trigger an index-out-of-bounds crash; add an explicit guard clause checking if (array.length < K) return 0; before running window calculations. Two Pointers
L3 · ③ Scale — “You need to find the maximum element in every window of size K in an array of size 10⁹. How do you do it in O(N) time and O(K) space?”
Trap: Store the window elements in a max-heap/priority queue.
Bar: Heap extraction and insertion take O(log K) time, giving O(N log K) overall; use a monotonic deque storing indices in descending order of their values, yielding amortized O(1) time per window slide. Two Pointers
L4 · ① Concurrency — “A real-time network stream sends metrics. We need to compute the 5-minute rolling average. How do you handle it under lock contention?”
Trap: Store incoming metrics in an array list and run a sliding window on it under a mutex.
Bar: Mutex contention on write bottlenecks the network threads; use a circular ring buffer with lock-free atomic pointers or a thread-safe sliding accumulator using atomic addition. Two Pointers
L5 · ⑥ Cost/Simplicity — “For a fixed window of size K, is sliding window (O(n)) always better than the brute-force nested loop (O(n * K))?”
Trap: Yes, O(n) is asymptotically smaller than O(n * K).
Bar: If K is extremely small (e.g. K <= 3), the nested loop has almost zero overhead and no extra state variables, outperforming sliding window due to compiler optimization and cache locality. Two Pointers
The floor keeps dropping: How do you adapt the sliding window to handle variable-size window constraints with O(1) auxiliary space?
Self-locate: died at L1 → you present mid-level; L4+ → staff signal.
Facing any new concept? Hit it with the six: concurrent? failing? at 100×? over time? adversarial? worth the cost? — that's the interviewer's whole playbook.
Play with it
Step through the sliding window yourself — press Play and predict each fork:
🤖 Don't fully get this? Learn it with Claude
Stuck on Introduction to Sliding Window Pattern? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Introduction to Sliding Window Pattern** (DSA) and want to truly understand it. Explain Introduction to Sliding Window Pattern from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Introduction to Sliding Window Pattern** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Introduction to Sliding Window Pattern** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Introduction to Sliding Window Pattern** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.