Sliding Window vs Two-Pointer — When to Use Which, and Why They're Confused
The subtle distinction between sliding window and two-pointer — one contiguous range that grows and shrinks, the other a pair converging on a relation — with the recognition signals that pick each one on a whiteboard.
Sliding window and two-pointer both use two indices walking a linear input. That's where the similarity ends. This post is the tie-breaker: what each technique preserves, when to reach for which, and why the "two-pointer" umbrella term causes half the confusion.
The verdict
Sliding window. Both indices move in the same direction, bracketing a contiguous range that grows and shrinks. The answer describes the range: longest, shortest, max sum, count of valid ranges.
Two-pointer (converging). Indices move in opposite directions, closing on a pair or triple satisfying a relation. The answer is the pair itself, or a boolean about symmetry.
Same shape on the whiteboard — two indices, a loop — but different invariants. The invariant is what tells you which one the problem wants.
When to reach for sliding window
- The answer is a contiguous subarray or substring.
- The constraint asks about the range's aggregate (sum ≥ target, no duplicate chars, contains all chars of
t). - Brute force is O(n²) with a nested loop over
(left, right)pairs. - The constraint's validity can be checked incrementally as elements enter or leave the window.
- You're looking for longest, shortest, max sum, or count of valid windows.
Canonical problems: LC 3 (Longest Substring Without Repeating Characters), LC 76 (Minimum Window Substring), LC 209 (Minimum Size Subarray Sum), LC 424 (Longest Repeating Character Replacement). See the sliding-window pattern post for the full walkthrough.
When to reach for converging two-pointer
- The answer is a pair (indices, values, or a symmetry boolean).
- Input is sorted or has a symmetric property (palindrome, reversed).
- Brute force is O(n²) trying every
(i, j)combination. - Moving one pointer safely eliminates a swath of pairs — the monotone invariant.
- You're checking a relation on the pair: sum equals target, container area, palindromic match.
Canonical problems: LC 11 (Container With Most Water), LC 15 (3Sum), LC 125 (Valid Palindrome), LC 167 (Two Sum II). See the two-pointer pattern post for the full walkthrough.
Side by side
| Aspect | Sliding window | Two-pointer (converging) |
|---|---|---|
| Pointer motion | Same direction (both forward) | Opposite directions (converging) |
| What you track | A contiguous range | A pair (or triple) |
| Input requirement | None | Sorted or symmetric |
| Killer invariant | Window validity flips exactly once as elements enter/leave | Moving the "worse" pointer never worsens the answer |
| Time | O(n) after any preceding sort | O(n) after any preceding sort |
| Auxiliary space | O(k) for window state (frequency map, sum) | O(1) |
| Answer shape | Length, sum, or count | Pair values or a boolean |
The worked comparison
Both techniques with two indices, both O(n) time, both operate on a linear input. The problem shape decides.
Sliding window — LC 3 Longest Substring Without Repeating Characters. Answer is a range ("longest contiguous substring…"). Two indices moving the same direction, bracketing the current candidate window:
def lengthOfLongestSubstring(s):
left = 0
seen = set()
best = 0
for right in range(len(s)):
while s[right] in seen:
seen.remove(s[left])
left += 1
seen.add(s[right])
best = max(best, right - left + 1)
return best
Two-pointer — LC 167 Two Sum II. Sorted input; answer is a pair summing to target. Two indices moving opposite directions, converging:
def twoSum(numbers, target):
left, right = 0, len(numbers) - 1
while left < right:
s = numbers[left] + numbers[right]
if s == target:
return [left + 1, right + 1]
elif s < target:
left += 1
else:
right -= 1
You could contort each to look like the other — a "sliding window" with left == right, or a "two-pointer" scan whose pointers never converge — but the code wouldn't read like the technique. Which invariant the problem hands you is the recognition signal; the code follows.
The confusing middle — same-direction "two-pointer" (fast/slow)
The vocabulary gets messy because there's a genuine third pattern: fast/slow pointers, both moving the same direction at different rates. Many resources file this under "two-pointer", which is why the umbrella term causes confusion.
- LC 141 Linked List Cycle: fast moves 2 steps, slow moves 1.
- LC 26 Remove Duplicates from Sorted Array: slow marks the write position, fast scans.
- LC 283 Move Zeroes: same shape.
Fast/slow is neither sliding window (no growing/shrinking range) nor converging two-pointer (no meeting point). It's its own pattern, and it gets its own post further down the roadmap.
Naming tip. When "two-pointer" is unqualified, it usually means converging. When someone means fast/slow, they usually say "fast/slow" or "tortoise and hare". Sliding window is always called sliding window.
Bottom line
Ask: what invariant does the problem hand me?
- A contiguous range whose validity is checkable incrementally → sliding window.
- A pair or triple satisfying a relation on sorted or symmetric input → converging two-pointer.
- In-place partitioning, cycle detection, or "write pointer trailing a read pointer" → fast/slow.
Name the invariant in one sentence and the technique picks itself. Miss the invariant and both techniques look identical — which is why the confusion exists in the first place.
For the O(n) time and O(k) vs O(1) space claims each pattern rests on, see the Big-O cheat sheet and the space complexity post.