Two Pointers and Sliding Window Cheat Sheet for Coding Interviews

- Fixed vs variable sliding window: pre-compute the first k elements for fixed size; expand right and shrink left for variable size
- "At most K" trick:
count(exactly K) = at_most(K) - at_most(K-1)unlocks problems where an exact-distinct constraint breaks a single window - Fast/slow pointer uses
isnot==: two nodes can hold the same value at different positions; check object identity or you get false positives - Converging two pointers requires a sorted array: the direction logic (left up for larger sum, right down for smaller) breaks without monotonicity
- Window length is
right - left + 1: the off-by-one in this formula silently drifts a fixed window every iteration - Variable sliding window is O(n) despite the nested loop: each element enters and leaves at most once, so total work is linear
Two Pointers and Sliding Window: A Cheat Sheet for the Next 30 Minutes
You have thirty minutes. Maybe less. You don't need a lecture right now. You need templates, the two or three signals that tell you which pattern to reach for, and a list of the bugs that will end your run silently without a stack trace.
That's this.
Which Pattern Is It, Actually?
Two pointers when:
- The array is sorted and you're hunting for a pair (or triplet) that meets a condition
- You're comparing the front and back of a string (palindrome, valid reverse)
- Two things move through the same structure at different rates (cycle detection, finding the middle)
- You're merging or comparing two separate sorted arrays in one pass
Sliding window when:
- The problem asks about a contiguous subarray or substring
- You're optimizing something (max, min, count) over a range of variable length
- The constraint involves the aggregate of everything inside the window
The clearest signal for sliding window: the words "subarray" or "substring" appear, and you're looking for a maximum, minimum, or count over some constraint. Two pointers doesn't require contiguity. Sliding window always does.
If you want the full breakdown of when one beats the other, Two Pointers vs Sliding Window has the analysis.
Two Pointers: Three Variants
Converging (Opposite Ends)
Sort the array first, then move inward. The invariant: everything left of left is too small; everything right of right is too large.
left, right = 0, len(arr) - 1 while left < right: total = arr[left] + arr[right] if total == target: return [left, right] elif total < target: left += 1 # need a larger sum else: right -= 1 # need a smaller sum
For 3Sum, wrap this in a for loop over the first element, then run converging two pointers on the rest. Skip duplicates to avoid redundant triplets: while left < right and arr[left] == arr[left - 1]: left += 1.
Classic problems: Two Sum II (167), 3Sum (15), Container With Most Water (11), Valid Palindrome (125).
Fast and Slow
Two pointers at different speeds. Fast moves 2x. They must meet inside any cycle.
slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow is fast: return True return False
Note is, not ==. You want object identity, not value equality. Two separate nodes can hold the same integer but sit at different positions. This distinction will absolutely betray you if you forget it at the wrong moment.
To find where the cycle starts (Linked List Cycle II, 142): after they meet, reset one pointer to head and advance both one step at a time. They meet at the cycle entry.
# after meeting in the cycle: slow = head while slow is not fast: slow = slow.next fast = fast.next return slow # cycle start
Classic problems: Linked List Cycle (141), Middle of Linked List (876), Find the Duplicate Number (287), Linked List Cycle II (142).
Parallel (Two Arrays)
One pointer per array, advance whichever is smaller.
i, j = 0, 0 result = [] while i < len(a) and j < len(b): if a[i] <= b[j]: result.append(a[i]) i += 1 else: result.append(b[j]) j += 1 result.extend(a[i:]) result.extend(b[j:])
One array runs out first. The leftover extend adds what remains. Easy to forget, always wrong without it. The interviewer will notice before you do.
Classic problems: Merge Sorted Array (88), Intersection of Two Arrays II (350).
Sliding Window: Two Variants
Fixed Size
The window always holds exactly k elements. Pre-compute the first window, then slide.
window_sum = sum(arr[:k]) result = window_sum for right in range(k, len(arr)): window_sum += arr[right] - arr[right - k] result = max(result, window_sum) return result
The element leaving is always arr[right - k]. If you write right - k - 1, you're removing the wrong element. Write it on scratch paper before you type a single character.
Classic problems: Maximum Average Subarray I (643), Sliding Window Maximum (239).
Variable Size (Shrink Left When Invalid)
Expand the right pointer by one each iteration. When the window becomes invalid, contract from the left until it's valid again. Then record the result.
left = 0 freq = {} result = 0 for right in range(len(s)): freq[s[right]] = freq.get(s[right], 0) + 1 # expand while len(freq) > k: # invalid: shrink freq[s[left]] -= 1 if freq[s[left]] == 0: del freq[s[left]] left += 1 result = max(result, right - left + 1) # valid window return result
Every element enters the window once and leaves at most once, so the total work is O(n) despite the nested loop. Left never moves backward. The inner while just advances it forward until the constraint is satisfied again.
For Minimum Window Substring (76), the structure is the same but you track when the window is valid (contains all required chars), not just when it's invalid:
left, have, need = 0, 0, len(counter) result, result_len = [-1, -1], float("inf") for right in range(len(s)): freq[s[right]] = freq.get(s[right], 0) + 1 if s[right] in counter and freq[s[right]] == counter[s[right]]: have += 1 while have == need: # window is valid: try to shrink if right - left + 1 < result_len: result = [left, right] result_len = right - left + 1 freq[s[left]] -= 1 if s[left] in counter and freq[s[left]] < counter[s[left]]: have -= 1 left += 1
Classic problems: Longest Substring Without Repeating Characters (3), Minimum Window Substring (76), Fruit Into Baskets (904), Max Consecutive Ones III (1004).
The "At Most K" Trick
Some problems can't be solved with a single window because the valid condition is exact: "exactly K distinct integers." You can't tell when to stop expanding for an exact constraint.
The trick: count(exactly K) = count(at most K) - count(at most K-1).
def at_most_k(arr, k): left, distinct, result = 0, 0, 0 freq = {} for right in range(len(arr)): if arr[right] not in freq or freq[arr[right]] == 0: distinct += 1 freq[arr[right]] = freq.get(arr[right], 0) + 1 while distinct > k: freq[arr[left]] -= 1 if freq[arr[left]] == 0: distinct -= 1 left += 1 result += right - left + 1 return result def exactly_k(arr, k): return at_most_k(arr, k) - at_most_k(arr, k - 1)
Classic problem: Subarrays with K Different Integers (992).
Quick Decision Table
| Problem type | Pattern | Key signal |
|---|---|---|
| Sorted array, pair meets target | Converging two pointers | "sorted" + pair |
| Palindrome check | Converging two pointers | compare front/back |
| 3Sum | Converging (inner) + loop (outer) | sorted + triplet |
| Linked list cycle | Fast/slow | cycle or middle |
| Find duplicate, values in [1, n] | Fast/slow | index-as-value trick |
| Merge two sorted arrays | Parallel pointers | two sorted inputs |
| Max/min over fixed k elements | Fixed sliding window | "size k" |
| Longest/shortest subarray, constraint | Variable sliding window | "at most" + "longest" |
| Count subarrays with exactly K | at_most_k(k) - at_most_k(k-1) | "exactly" + count |
Bugs That Silently Break Things
![Who Wants to Be a Millionaire contestant looking alarmed at a question asking "What is the value in the variable?" with answer options: [object Object], "42", undefined, NaN](https://assets.spacecomplexity.ai/blog/content-images/two-pointers-sliding-window-cheat-sheet/1781908485876-silent-bug-variable-value.jpg)
The variable has a value. You just have no idea which one it is.
These produce wrong output with no crash. They survive basic testing and detonate on a hidden edge case. For the full catalog, Two-Pointer Bugs and Sliding Window Bugs are worth bookmarking before your next round.
Advancing only one pointer after a match. In converging two pointers, finding the target means you do left += 1 and right -= 1 together. Advancing only one re-examines the same pair forever. For 3Sum and similar, also skip duplicate values before moving on or you'll return duplicate triplets that look correct on the happy path and fail on any de-dup test case.
Off-by-one in window size. Elements from left to right inclusive is right - left + 1, not right - left. If you check == k with the wrong formula, your "fixed" window drifts every iteration. One off. Silent. Will fail on any input where the answer isn't in the first window.
Forgetting to clean up the frequency map. After decrementing a count to zero, delete the key. Otherwise len(freq) counts characters no longer in the window and the validity check breaks. Your window will shrink too aggressively. The output looks almost right on short strings and completely falls apart on longer ones.
Sorting assumption. Converging two pointers on sum problems requires a sorted array. The direction logic depends on monotonicity. On an unsorted array, you get garbage output with no error to tell you why.
Using == instead of is for linked list nodes. In Python and Java, two nodes at different positions can hold the same value. slow == fast might trigger on value equality. slow is fast checks object identity. This is the bug that makes your cycle detector return true on a straight list with repeated values.
Complexity
Both patterns run in O(n) time. Each element is processed at most twice. Space is O(1) with just pointers, but most variable window problems need a hash map for counts, which pushes space to O(k) where k is the alphabet or distinct value count.
Practice Under Pressure
Knowing a template and executing it while talking through your reasoning are different skills. The template lives in your head. Execution breaks down when you're on the clock, an interviewer is watching, and you second-guess whether the window size formula is right - left or right - left + 1.
One session narrating your sliding window logic out loud will expose gaps that three hours of silent LeetCode won't. SpaceComplexity simulates voice-based interviews and scores your explanation and reasoning, not just whether the code compiles.
Further Reading
- LeetCode Two Pointers problem set, Every problem tagged by pattern, filterable by difficulty
- LeetCode Sliding Window problem set, Full set of canonical problems to drill
- GeeksforGeeks: Two Pointer Technique, Conceptual walkthrough with examples
- GeeksforGeeks: Sliding Window Maximum, The deque-based O(n) solution for a common fixed-window hard problem
- Wikipedia: Sliding window protocol, Origin of the term, flow control context