Two Pointers and Sliding Window Interview Patterns
Two pointers and sliding window are techniques that turn O(n²) brute-force solutions into O(n). They are among the most frequently tested patterns in coding interviews. This guide covers both techniques with clear templates and the problem types each solves.
Two Pointers: Opposite Direction
Start pointers at both ends of a sorted array and move them toward each other based on the comparison result.
# Two Sum II (sorted array, LeetCode 167):
def two_sum_sorted(nums, target):
l, r = 0, len(nums) - 1
while l < r:
s = nums[l] + nums[r]
if s == target: return [l+1, r+1]
elif s < target: l += 1 # need larger sum, move left ptr right
else: r -= 1 # need smaller sum, move right ptr left
return []
# Valid Palindrome: same idea — compare from both ends, move inward
Three Sum (LeetCode 15)
def three_sum(nums):
nums.sort()
result = []
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i-1]: continue # skip duplicate
l, r = i + 1, len(nums) - 1
while l < r:
s = nums[i] + nums[l] + nums[r]
if s == 0:
result.append([nums[i], nums[l], nums[r]])
while l < r and nums[l] == nums[l+1]: l += 1 # skip duplicates
while l < r and nums[r] == nums[r-1]: r -= 1
l += 1; r -= 1
elif s < 0: l += 1
else: r -= 1
return result
# Time O(n log n + n^2) = O(n^2), Space O(1)
Two Pointers: Same Direction (Fast/Slow)
Two pointers move in the same direction at different speeds. Used for linked list cycle detection and in-place array manipulation.
# Remove Duplicates from Sorted Array (LeetCode 26):
def remove_duplicates(nums):
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1
# Floyd cycle detection (linked list):
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast: return True
return False
Sliding Window: Fixed Size
Maintain a window of exactly k elements, slide it one position at a time.
# Maximum Average Subarray of Length K (LeetCode 643):
def find_max_average(nums, k):
window_sum = sum(nums[:k])
max_sum = window_sum
for i in range(k, len(nums)):
window_sum += nums[i] - nums[i-k] # add new, remove old
max_sum = max(max_sum, window_sum)
return max_sum / k
# Time O(n), Space O(1)
Sliding Window: Variable Size
Expand right pointer to grow the window; shrink left pointer when the constraint is violated.
# Longest Substring Without Repeating Characters (LeetCode 3):
def length_of_longest_substring(s):
char_set = set()
l = max_len = 0
for r in range(len(s)):
while s[r] in char_set: # constraint violated: remove from left
char_set.remove(s[l])
l += 1
char_set.add(s[r])
max_len = max(max_len, r - l + 1)
return max_len
# Minimum Window Substring (LeetCode 76):
from collections import Counter
def min_window(s, t):
need = Counter(t)
missing = len(t)
l = best_l = best_r = 0
for r, c in enumerate(s, 1):
if need[c] > 0: missing -= 1
need[c] -= 1
if missing == 0: # valid window found
while need[s[l]] < 0: # shrink from left
need[s[l]] += 1
l += 1
if best_r == 0 or r - l < best_r - best_l:
best_l, best_r = l, r
need[s[l]] += 1
missing += 1
l += 1
return s[best_l:best_r]
Common Problem Patterns
| Problem | Pattern | LeetCode |
|---|---|---|
| Two sum on sorted array | Two pointers, opposite | 167, 15, 16, 18 |
| Container with most water | Two pointers, opposite | 11 |
| In-place array modification | Two pointers, same direction | 26, 27, 80, 283 |
| Linked list cycle | Fast/slow pointers | 141, 142, 287 |
| Max/min of fixed window | Sliding window, fixed | 643, 239 |
| Longest substring with constraint | Sliding window, variable | 3, 159, 340, 424 |
| Minimum window containing target | Sliding window, variable | 76, 567, 438 |
Variable Window Template
def sliding_window(s, k):
l = 0
state = {} # track window state (character counts, etc.)
result = 0
for r in range(len(s)):
# Expand: add s[r] to window state
state[s[r]] = state.get(s[r], 0) + 1
# Shrink: while constraint violated, remove s[l]
while len(state) > k: # example constraint: at most k distinct chars
state[s[l]] -= 1
if state[s[l]] == 0: del state[s[l]]
l += 1
# Update result (window is valid here)
result = max(result, r - l + 1)
return result
Interview Tips
- For any “longest/minimum subarray/substring” problem, try sliding window first
- The variable window shrink loop (while constraint violated) is the key — practice writing it from memory
- For three sum: sort first, fix one pointer, use two-pointer for the pair — this is the standard O(n²) approach
- Fast/slow pointer: the distance between them at cycle entry = distance from head to cycle entry (used in LeetCode 142)
- State your time complexity explicitly: “O(n) because each element enters and leaves the window at most once”