Variable-size window
In one line
Grow the window with a right pointer and shrink it with a left pointer only when needed, so you find the shortest or longest valid subarray in one O(n) pass.
What it is
A variable-size window has two pointers, left and right, that both only move forward. The right pointer adds elements; the left pointer removes them. The window's length changes as the scan goes.
It answers questions like 'shortest subarray with sum at least target' or 'longest substring with at most k zeros'. The key requirement is monotonic validity: making the window bigger moves it in one direction only (for example, the sum only goes up because all numbers are positive), so once you shrink past a point you never need to come back to it.
Why interviewers ask
This is one of the most common medium-level patterns in coding interviews. It tests whether you can turn an O(n^2) 'try every subarray' brute force into O(n), explain why each element is added and removed at most once, and state the condition that makes the technique valid. Many candidates code it from memory but cannot say why it fails with negative numbers; explaining that is what stands out.
How it works
- 1Start with left = 0 and an empty window state (a sum, a count of zeros, a map of counts).
- 2Move right from 0 to n-1. Each step, add nums[right] to the state.
- 3Shortest-valid problems: while the window is valid, record its length, then remove nums[left] and move left forward. You keep shrinking to find the smallest valid window ending at right.
- 4Longest-valid problems: while the window is invalid, remove nums[left] and move left forward. Once valid again, record the length.
- 5Because left only moves forward and never passes right by more than one, the inner while loop runs at most n times in total across the whole scan. That makes the total O(n), even though there is a loop inside a loop.
Worked example · Shortest subarray with sum >= 7 in [2, 3, 1, 2, 4, 3]
- right = 0..2: sum grows 2, 5, 6. Never >= 7, so no shrinking. left = 0.
- right = 3 (add 2): sum = 8 >= 7. Record length 4 (indices 0..3). Remove nums[0] = 2: sum = 6, left = 1. Now invalid, stop shrinking.
- right = 4 (add 4): sum = 10. Record length 4 (1..4). Remove 3: sum = 7, left = 2, still valid: record length 3 (2..4). Remove 1: sum = 6, left = 3, stop.
- right = 5 (add 3): sum = 9. Record length 3 (3..5). Remove 2: sum = 7, left = 4, valid: record length 2 (4..5, which is [4, 3]). Remove 4: sum = 3, left = 5, stop.
- Answer: 2. Each of the 6 elements entered once and 5 of them left once: about 11 pointer moves instead of checking all 21 subarrays.
Picture it
It is like a caterpillar crawling along a branch: the head (right) stretches forward to grab more, and the tail (left) pulls in only when the body is longer than it needs to be. Neither end ever goes backwards.
Reference code
def min_subarray_len(target, nums):
"""Length of the shortest subarray with sum >= target.
Requires all numbers to be positive."""
left = 0
window = 0
best = float("inf")
for right, x in enumerate(nums):
window += x # grow: take nums[right]
while window >= target: # valid: record, then shrink
best = min(best, right - left + 1)
window -= nums[left]
left += 1
return 0 if best == float("inf") else best
def longest_ones(nums, k):
"""Longest run of 1s if you may flip at most k zeros."""
left = zeros = best = 0
for right, x in enumerate(nums):
zeros += (x == 0)
while zeros > k: # invalid: shrink until valid
zeros -= (nums[left] == 0)
left += 1
best = max(best, right - left + 1) # record after fixing
return best
Both functions are O(n) time and O(1) space because left only moves forward. Note where the answer is recorded: inside the shrinking loop for 'shortest valid', after it for 'longest valid'.
Key patterns
Shortest valid window
When: Minimum length subarray whose sum is at least target (positive numbers), smallest substring containing something
How: After adding right, shrink while valid and record inside the while loop.
Longest valid window
When: Longest run of 1s with at most k flips, longest subarray with sum at most k (non-negative numbers), fruit into baskets
How: After adding right, shrink while invalid, then record right - left + 1.
Budget window
When: Longest window where the 'cost' (zeros, replacements, distinct types) stays within a budget k
How: Track the cost as elements enter and leave; shrink while cost > k.
Counting windows
When: Number of subarrays satisfying a monotonic condition, such as product less than k
How: For each right, all windows from left..right to right..right are valid, so add right - left + 1. See the at-most-K page.
Real-world use
TCP flow control
TCP's sliding window lets a sender have only a limited number of unacknowledged bytes in flight. The right edge advances as the receiver advertises more space and the left edge advances as acknowledgements arrive, so the window size changes over time.
Log and session analysis
Analytics pipelines find the shortest time span that contains N errors, or group events into user sessions that end after a gap of inactivity, by moving a start and end pointer over time-sorted events.
Video and audio buffering
Players such as YouTube's keep a buffer window ahead of the playhead: they fetch more when the buffer is short and stop when it reaches a target length, growing and shrinking a range of downloaded segments.
Genomics
Tools scanning DNA look for the shortest region containing certain markers, or the longest region with GC content under a threshold, using two pointers over the sequence instead of testing every region.
In a project
In a payments monitoring system you must flag a card as suspicious if any period shorter than 10 minutes contains spending of at least 2,000 dollars, scanning millions of time-sorted transactions per day.
- Sort each card's transactions by time (they usually arrive sorted) and keep left and right pointers plus a running total.
- For each new transaction at right, add its amount to the total.
- While the time from transactions[left] to transactions[right] is 10 minutes or more, subtract transactions[left] and move left forward.
- If the remaining total is at least 2,000, raise a flag with the exact window (left to right) as evidence.
- Run this per card in the streaming job, keeping only the current window in memory.
Impact: Each transaction is added and removed once, so the check is O(n) per card and keeps up with the live stream, instead of an O(n^2) scan that would fall hours behind. The window gives investigators the exact burst of transactions that triggered the flag.
Trade-offs
Use it when
- You need a contiguous subarray or substring and the answer is a shortest or longest length (or a count)
- Validity is monotonic: growing the window can only push it one way (sums of positive numbers, counts of a property)
- You want O(n) instead of checking every pair of endpoints
Avoid it when
- Numbers can be negative and the condition is on the sum: shrinking may make the sum bigger, so use prefix sums with a hash map (exact sum) or a monotonic deque over prefix sums (at least k)
- Elements need not be contiguous (subsequences): that is usually dynamic programming
- The window length is fixed: a fixed window is simpler
Cost: Time O(n): each index enters once and leaves at most once (amortised). Space O(1) for sums and counters, O(distinct values) if you keep a map.
Common mistakes
- Applying it to arrays with negative numbers for sum conditions, where shrinking does not reliably reduce the sum.
- Recording the answer in the wrong place: for shortest windows record inside the while loop, for longest after it.
- Using 'if' instead of 'while' when shrinking, so the window is only shrunk by one element per step.
- Saying the nested loop is O(n^2); the inner loop moves left forward at most n times in total.
- Forgetting the 'no valid window' case (returning infinity instead of 0).
Interview questions
Quick revision
- Two pointers, both only move forward; window = nums[left..right].
- Shortest valid: shrink while valid, record inside the loop.
- Longest valid: shrink while invalid, record after the loop.
- O(n) amortised: each element enters once and leaves once.
- Needs monotonic validity (for sums: non-negative numbers).
- Negatives + sum target: prefix sums + hash map, or deque on prefix sums.
- Window length = right - left + 1.
Check yourself
1. For nums = [1, 1, 1, 4, 2] and target 6, what is the shortest subarray length with sum >= 6?
2. Why does the variable window fail for 'shortest subarray with sum >= k' when numbers can be negative?
3. In a 'longest valid window' problem, when should you record the answer?
4. What is the total time complexity of the grow-and-shrink loop?
Related
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.