The Sliding Window Technique Explained
The sliding window technique tracks a moving subrange of an array or string, turning many O(n²) brute-force problems into a single O(n) linear pass.
The sliding window technique is an algorithmic pattern for problems that ask something about every contiguous subrange of an array or string — the longest substring without repeats, the maximum sum of any k consecutive elements, the smallest subarray summing to at least a target. Instead of re-examining each subrange from scratch, it maintains a running “window” of elements and slides its edges forward, reusing work from the previous position instead of redoing it.
The payoff is asymptotic: a brute-force approach that checks every possible subrange is typically O(n²) or worse, while the sliding window equivalent is usually O(n) — a single pass through the input.
The brute-force baseline
Consider finding the maximum sum of any k consecutive elements in an array. The naive approach checks every possible window from scratch:
def max_sum_bruteforce(nums, k):
best = float("-inf")
for i in range(len(nums) - k + 1):
window_sum = sum(nums[i:i+k]) # recomputes the whole sum every time
best = max(best, window_sum)
return best
This is O(n × k) — for each of the roughly n starting positions, it re-sums k elements even though consecutive windows overlap almost entirely.
The sliding window version
The insight: moving the window forward by one position only changes two elements — the one that just left the window and the one that just entered it. Instead of recomputing the whole sum, adjust it incrementally:
def max_sum_sliding_window(nums, k):
window_sum = sum(nums[:k])
best = window_sum
for i in range(k, len(nums)):
window_sum += nums[i] - nums[i - k] # add the new element, drop the old one
best = max(best, window_sum)
return best
Same result, but now each step does constant work instead of re-summing k elements, bringing the total down to O(n).
Fixed-size vs variable-size windows
The example above is a fixed-size window — the window’s width (k) is constant, and both edges move forward together in lockstep. This variant is a straightforward adjustment: add the incoming element, subtract the outgoing one.
Many problems instead need a variable-size window, where the window grows or shrinks based on a condition. The classic example is finding the longest substring without repeating characters:
def longest_unique_substring(s):
seen = set()
left = 0
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
Here right always advances one step at a time, but left only advances when the window contains a duplicate — shrinking the window from the front until the duplicate is gone. Both pointers move forward only, never backward, which is what keeps this O(n) overall despite the nested-looking loop: across the entire run, left and right each advance at most n times total, not n times per outer iteration.
Recognizing when it applies
Sliding window problems share a few telltale features:
- The input is a linear structure — an array or string.
- The question is about a contiguous subrange (subarray or substring), not any arbitrary subset.
- There’s a monotonic property: as the window grows, some tracked quantity (a sum, a count, a set of characters) only grows or only shrinks — never both — which is what guarantees the pointers don’t need to backtrack.
If a problem talks about “subsequence” rather than “subarray” or “substring,” it likely isn’t contiguous, and sliding window probably doesn’t apply directly — that’s usually a sign to look at dynamic programming instead.
Sliding window vs two pointers
Sliding window is often described as a special case of the broader two-pointer technique, and the line between them can blur:
| Sliding window | General two pointers | |
|---|---|---|
| Structure tracked | A contiguous range between two indices | Any two positions, not necessarily forming a range |
| Typical use | Subarray/substring sum, count, or uniqueness problems | Sorted-array pair sums, palindrome checks, merging |
| Movement pattern | Both pointers generally move forward only | Pointers may move toward each other or independently |
In practice, if you’re tracking a running aggregate (sum, character counts, a max) over a contiguous range and adjusting it incrementally as the range’s edges move, you’re using a sliding window regardless of what it’s called in a given explanation.
Where it shows up
Beyond interview problems, the same idea underlies real systems: rate limiters that count requests in the last N seconds use a sliding time window rather than recomputing from all history on every request — the same incremental-update principle as the array version, just with time as the axis instead of an index. TCP’s flow control also uses a sliding window to track how much unacknowledged data can be in flight at once.
The takeaway
The sliding window technique replaces repeated recomputation over overlapping subranges with incremental updates as a window’s edges move forward — turning many O(n²) brute-force solutions into O(n) linear passes. Reach for it whenever a problem asks about contiguous subarrays or substrings and involves a running aggregate with a monotonic property; use a fixed-size window when the range width is constant, and a variable-size window with a shrink condition when it isn’t.
Keep reading
The Lycoris Team · · 4 min read The KMP Algorithm: Fast String Matching Explained
The Knuth-Morris-Pratt algorithm finds a pattern inside a text in linear time by never re-examining characters it has already matched.
The Lycoris Team · · 5 min read What Is a Ring Buffer?
A ring buffer is a fixed-size array that wraps its read and write pointers around, giving O(1) enqueue and dequeue without ever resizing.
The Lycoris Team · · 5 min read Fenwick Trees (Binary Indexed Trees), Explained
A Fenwick tree, or binary indexed tree, answers prefix-sum queries and point updates in O(log n) with far less memory than a segment tree.