Imagine an AI monitoring a live video feed for suspicious activity. It cannot rewatch the entire recording every second - it needs to slide a window of attention across the stream, keeping track of just enough context. That is exactly what the two pointers and sliding window patterns do for sequential data.
Both techniques turn brute-force O(nยฒ) scans into elegant O(n) passes.
Place two references into a sequence and move them according to a rule. There are two main flavours.
Start one pointer at each end and move them inward. Perfect for sorted data.
Classic problem - Pair Sum: Given a sorted array, find two numbers that add to a target.
function pairSum(arr, target):
left = 0
right = arr.length - 1
while left < right:
sum = arr[left] + arr[right]
if sum == target: return (left, right)
if sum < target: left += 1
else: right -= 1
return null
Because the array is sorted, moving left right increases the sum, and moving right left decreases it - guaranteeing we never miss a valid pair.
Both pointers move forward, but at different speeds. Useful for partitioning or cycle detection.
Example - Remove duplicates in-place: A slow pointer marks the write position; a fast pointer scans ahead.
function removeDuplicates(arr):
slow = 0
for fast in 1..arr.length - 1:
if arr[fast] != arr[slow]:
slow += 1
arr[slow] = arr[fast]
return slow + 1
In the converging two-pointer technique on a sorted array, what should you do when the current sum is less than the target?
Sign in to join the discussion
A sliding window maintains a contiguous sub-sequence of size k (fixed) or variable length, updating aggregates incrementally instead of recalculating from scratch.
Slide a window of exactly k elements across the array.
Example - Maximum sum of any k consecutive elements:
function maxSubarraySum(arr, k):
windowSum = sum(arr[0..k-1])
best = windowSum
for i in k..arr.length - 1:
windowSum += arr[i] - arr[i - k]
best = max(best, windowSum)
return best
Each step adds the new element entering the window and subtracts the one leaving - O(1) per move.
Expand the window to include more data, then shrink it when a constraint is violated.
Example - Longest substring without repeating characters:
function longestUnique(s):
seen = {}
left = 0
best = 0
for right in 0..s.length - 1:
if s[right] in seen and seen[s[right]] >= left:
left = seen[s[right]] + 1
seen[s[right]] = right
best = max(best, right - left + 1)
return best
| Pattern | AI Use Case | |---------|------------| | Fixed window | Anomaly detection over the last k sensor readings | | Variable window | Finding the shortest text span containing all query keywords (search engines) | | Converging pointers | Efficient nearest-neighbour checks in sorted embedding dimensions | | Same-direction pointers | Merging sorted result lists from multiple retrieval models |
Which pattern would you choose to find the smallest subarray whose sum exceeds a given value?
What is the typical time complexity achieved by converting a brute-force nested loop into a two-pointer or sliding-window solution?