Two Pointers and Sliding Window Cheat Sheet for Coding Interviews

June 19, 20269 min read
dsaalgorithmsinterview-prepleetcode
Two Pointers and Sliding Window Cheat Sheet for Coding Interviews
TL;DR
  • 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 is not ==: 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
<!-- cover: cover-source.jpg -->

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 typePatternKey signal
Sorted array, pair meets targetConverging two pointers"sorted" + pair
Palindrome checkConverging two pointerscompare front/back
3SumConverging (inner) + loop (outer)sorted + triplet
Linked list cycleFast/slowcycle or middle
Find duplicate, values in [1, n]Fast/slowindex-as-value trick
Merge two sorted arraysParallel pointerstwo sorted inputs
Max/min over fixed k elementsFixed sliding window"size k"
Longest/shortest subarray, constraintVariable sliding window"at most" + "longest"
Count subarrays with exactly Kat_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

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