LeetCode Patterns: Sliding Window Technique
Master fixed-size and variable-size sliding window patterns. Solve Maximum Sum Subarray, Longest Substring Without Repeating Characters, Minimum Window Substring, and Permutation in String.
If you have ever caught yourself writing a double for loop to inspect every contiguous subarray, sliding window is probably the optimisation you are missing. It turns an $O(nk)$ or $O(n^2)$ scan into a single linear pass by reusing the work it has already done. This article walks through the technique from first principles, then drills four canonical LeetCode problems plus a monotonic-deque variant.
1. The Idea in One Picture
A sliding window is a contiguous range [left, right] over an array or string. Instead of recomputing everything when the range moves, we add the element entering on the right and remove the element leaving on the left. Each element is touched at most twice, so the total cost is $O(n)$.
There are two flavours:
- Fixed-size window — width is a constant
kand both pointers advance together. - Variable-size window —
rightexpands to grow the window,leftcontracts to repair it when an invariant breaks.
A picture is worth a thousand words. Below, the green cells are the live window and the right column shows the incremental sum update.

The orange cell is entering the window; the pink one is leaving. Notice the right column: each step is one subtraction and one addition, never a fresh sum() call. That is the entire trick.
2. Fixed-Size Window — Maximum Sum Subarray of Size K
Problem. Given an array arr and an integer k, find the maximum sum of any contiguous subarray of length k.
Naive approach. Sum every length-k slice — $O(nk)$.
Sliding window. Compute the first window’s sum, then slide one position at a time, subtracting the element that leaves and adding the element that enters.
| |
Java
| |
Complexity. Time $O(n)$ — one addition and one subtraction per slide. Space $O(1)$.
Why it works. The window’s sum is a function of its multiset of elements. When the window shifts by one, the multiset changes by exactly one removal and one insertion, so we only need to apply that delta. This incremental update generalises to running counts, frequency maps, and even monotonic deques (Section 6).
3. Variable-Size Window — Two Postures
When the window size is not given, we use two pointers and let the problem dictate the policy:
- Find the longest valid window: keep expanding
right; whenever the window goes invalid, contractleftuntil it is valid again, then record the length. - Find the shortest valid window: keep expanding
rightuntil the window becomes valid; then contractleftas far as possible while staying valid, recording the length on each shrink.
The picture below traces the longest-substring-without-repeating-characters algorithm on "abcabcbb". Green = valid (expanding), pink = duplicate detected, purple = contracting to repair.

And here is what the window length looks like over time. Green bands are expansion phases, purple bands are contraction phases.

The crucial invariant: left only ever moves right. So even though there is a while inside a for, the inner loop’s total work across the whole run is bounded by n. That is the amortised-$O(n)$ argument.
4. LeetCode 3 — Longest Substring Without Repeating Characters
Problem. Given a string s, return the length of the longest substring with all distinct characters.
Pattern. Variable window, find longest. The invariant is “all characters in the window are unique”. When s[right] is already in the window, slide left past its previous occurrence.
| |
Java
| |
Complexity. Time $O(n)$ — each character is inserted into and removed from the map at most once. Space $O(\min(n, |\Sigma|))$ where $|\Sigma|$ is the alphabet size.
Two implementation styles. The version above uses jump-left — left leaps directly past the previous occurrence. The textbook alternative is step-left: hold a frequency map and while count[s[right]] > 1: drop(s[left]); left += 1. Both are $O(n)$; jump-left is one line shorter, step-left generalises better to “at most k” variants.
5. LeetCode 76 — Minimum Window Substring
Problem. Given s and t, return the shortest substring of s that contains every character of t (with multiplicities). Return "" if no such window exists.
Pattern. Variable window, find shortest. The invariant is “the window contains all required characters at the required counts”. We expand until the invariant holds, then aggressively contract.
The cleanest way to track validity is a valid counter — the number of distinct required characters whose count in the window has reached the required threshold. The window is valid iff valid == len(need). This avoids comparing two hash maps on every step.
| |
Java
| |
Complexity. Time $O(|s| + |t|)$ — both pointers advance monotonically. Space $O(|\Sigma|)$.
The subtle line is if have[drop] == need[drop]: valid -= 1 — we decrement valid before the have count actually falls below the threshold, because we’re about to make it fall. Get the order wrong and the counter desyncs.
6. LeetCode 567 — Permutation in String
Problem. Return True iff some substring of s2 is a permutation of s1.
Pattern. Fixed-size window of width len(s1). Two strings are permutations of each other iff their character-frequency vectors are equal. So we maintain a length-26 frequency vector for the current window in s2 and compare to the frequency vector of s1.
| |
Java
| |
Complexity. Time $O(m)$ — every slide is a constant amount of work because we maintain matches incrementally instead of rescanning all 26 buckets. Space $O(1)$.
The same skeleton solves LC 438 (Find All Anagrams) verbatim — just collect the start indices instead of returning early.
7. Variant: Sliding Window Maximum (Monotonic Deque)
When the window asks for the min or max of its elements, the simple “add / remove” trick is not enough — removing the current max means we need to know the next-best element instantly. The standard tool is a monotonic deque that stores values (or indices) in strictly decreasing order. The front of the deque is always the current window max.

| |
Each index is pushed and popped at most once, so total work is $O(n)$ — the same linear bound as the basic patterns.
8. When to Reach for Sliding Window
The decision tree below summarises which flavour fits which problem shape:

Reach for sliding window when all of the following are true:
- The answer is over a contiguous range (subarray / substring).
- The window’s value can be maintained incrementally as the boundaries move (running sum, frequency map, monotonic deque, …).
- The objective rewards either a specific size (fixed
k) or a boundary condition (longest valid / shortest valid).
If any of those fails — non-contiguous picks, requiring a global view, or needing to undo arbitrary insertions — try prefix sums, two pointers on sorted data, dynamic programming, or a heap instead.
9. Templates Worth Memorising
Fixed-size window
| |
Variable window — longest valid
| |
Variable window — shortest valid
| |
10. Common Pitfalls
- Off-by-one in length. Window length is
right - left + 1, notright - left. - Updating
validafter the count change. Incrementvalidexactly whenhave[c]first equalsneed[c]; decrement before it falls below. The order matters. - Using
list.pop(0)for fixed windows. That is $O(k)$. Usecollections.deque(or simple index arithmetic) for $O(1)$. - Recomputing the window from scratch. The whole point is incremental update — if your inner loop scans the window, you have lost the asymptotic improvement.
- Negative numbers in “sum ≥ target”. Contracting may not reduce the sum, so the “shortest valid” template doesn’t apply directly. Reach for prefix sums + monotonic deque, or a different framing.
11. Practice Set
| Difficulty | Problem | Pattern |
|---|---|---|
| Easy | LC 643 Maximum Average Subarray I | fixed |
| Easy | LC 1456 Max Vowels in a Substring of Given Length | fixed |
| Medium | LC 3 Longest Substring Without Repeating Characters | longest |
| Medium | LC 159 / 340 Longest Substring with At Most K Distinct | longest |
| Medium | LC 209 Minimum Size Subarray Sum | shortest |
| Medium | LC 424 Longest Repeating Character Replacement | longest |
| Medium | LC 567 Permutation in String | fixed |
| Medium | LC 438 Find All Anagrams in a String | fixed |
| Medium | LC 713 Subarray Product Less Than K | longest |
| Medium | LC 1004 Max Consecutive Ones III | longest |
| Hard | LC 76 Minimum Window Substring | shortest |
| Hard | LC 239 Sliding Window Maximum | monotonic deque |
| Hard | LC 30 Substring with Concatenation of All Words | fixed + hashing |
12. Summary
Sliding window is fundamentally an amortisation argument: instead of recomputing each window’s value from scratch, you maintain a tiny piece of state and update it as the window moves by one position. Two pointers, both monotonically increasing, give you an $O(n)$ algorithm where the brute force is $O(n^2)$.
The mental checklist when you see a contiguous-range problem:
- Is the window size fixed or variable?
- If variable, am I after the longest or the shortest valid window?
- What state do I maintain — a sum, a frequency map, a monotonic deque?
- What is the invariant that determines validity, and which pointer’s move can break it?
Internalise those four questions and most sliding window problems collapse to filling in a template.