Algorithmic Patterns
Two pointers and sliding window
Scanning from both ends and maintaining a window
The brute-force way to find the best contiguous stretch of an array is to try every start, and from each start re-add every element of every window - work, most of it repeated. The trick is embarrassingly simple: keep a window and slide it. When it moves one step right, you already know almost everything about the new window - only one element left and one joined. Update the answer in instead of re-reading. Do that across the array and the whole scan is .
The idea in one line
Four windows, one trick
"Two pointers" is a family. Every member keeps two indices and advances them by a rule instead of nesting loops. Four shapes cover almost everything you'll meet:
- Opposite endsPointers start at both ends of a sorted array and walk inward - the pattern behind two-sum, container-with-most-water, and palindrome checks.
- Fast / slowTwo pointers over the same sequence at different speeds - cycle detection (Floyd's tortoise-and-hare) and finding a list's middle in one pass.
- Fixed windowBoth pointers stay a constant distance apart and slide together - max sum of consecutive items, moving averages. This is the demo below.
- Variable windowThe right pointer expands greedily; the left pointer shrinks to restore an invariant - longest substring without repeats, smallest window covering a target.
Slide the window yourself
Below is a fixed-size window of width over an array. Your goal: find the window with the largest sum. Drag the highlighted window left or right, or use the step buttons - and watch the two counters. Each single slide costs the sliding-window method just two operations (drop the element leaving on the left, add the one entering on the right), while a brute-force scan re-adds all elements from scratch every time. The green underline marks the best window found so far. Press Auto-scan to sweep the whole array and read off the final cost gap.
First window [0...2] costs 3 additions to sum - identical for both methods. Now slide it and watch the cost diverge.
The whole lesson is in that counter gap
Why it's O(n), in symbols
Let be the sum of the window starting at index . The next window shares all but two elements, so
That's one recurrence, per step. Sweeping every window costs (the first sum) plus (two ops per slide) . Brute force instead recomputes each of the windows from scratch: work. The same -patch idea generalizes to prefix sums - precompute once in , then any window sum is in constant time, no sliding required.
In code
The fixed window is a one-line update inside a single loop. The variable window looks like a nested loop but isn't - each pointer marches forward at most times total, so the whole thing is still linear.
# Fixed window: max sum of any k consecutive items - O(n)
def max_window_sum(a, k):
s = sum(a[:k]) # first window: k additions, unavoidable
best = s
for r in range(k, len(a)):
s += a[r] - a[r - k] # add entering, drop leaving: O(1)
best = max(best, s)
return best
# Variable window: longest substring with no repeats - O(n)
def longest_unique(s):
seen = {} # char -> last index it appeared
left = best = 0
for right, ch in enumerate(s):
if ch in seen and seen[ch] >= left:
left = seen[ch] + 1 # shrink: jump left past the repeat
seen[ch] = right
best = max(best, right - left + 1)
return bestWhen the pattern applies - and when it lies
Check yourself
When a fixed window slides one step right, how should the running sum update?
What kind of problem is the sliding-window pattern actually for?
What invariant makes a variable-size window run in O(n) despite its nested-looking loop?
Recall the loop invariant. Try to state it, then check.
Lock it in
- The whole trick is to never re-read what you already read: patch the window summary for the one element that entered and the one that left, per slide.
- Two pointers that only move forward touch each index a constant number of times, turning an scan into .
- One trick, four shapes: opposite ends, fast/slow, fixed window, and variable (grow-then-shrink) window.
- It applies only to contiguous subarrays or substrings whose answer moves monotonically; negative numbers break the shrink rule, so reach for prefix sums instead.
Primary source
Ask your teacher