Fixed-size windows slide at a constant width. But what if the question is: "find the shortest subarray whose sum reaches a target"?
The answer could be 2 elements long or 6. You do not know the width in advance.
Consider the array [2, 3, 1, 2, 4, 3] with target sum 7. The subarray [2, 3, 1, 2] sums to 8 --- that works, but is it the shortest? What about [4, 3]? That sums to 7 in only 2 elements. You cannot discover this with a fixed window because you would need to try every possible width from 1 to 6, for every possible starting position. That is O(n^2) subarrays, each requiring up to O(n) time to sum.
What would you do? Check every possible subarray? With n elements there are n*(n+1)/2 of them. For a 10,000-element array, that is roughly 50 million subarrays. There has to be a better way.
What if there were a way to avoid starting over? What if you could grow a subarray that's too small and trim one that's big enough --- without ever going backwards? That would mean each element gets examined a constant number of times, not once per starting position.
Time to get your hands dirty. You have the array [2, 3, 1, 2, 4, 3] and a target sum of 7. Somewhere in that array is the shortest subarray that hits the target --- but you don't know where it starts, where it ends, or how wide it is.
The brute-force instinct says: try every possible start, try every possible end, compute the sum. That is O(n^2) subarray checks. For this tiny 6-element array, that is manageable. For a 10,000-element array in an interview? You will time out.
But notice something about consecutive subarrays. If [2, 3, 1] sums to 6 (too small), you don't need to throw it all away and start fresh. You could extend the right end to grab the next element. And if [2, 3, 1, 2] sums to 8 (big enough), maybe you can trim from the left and still stay above 7. In the previous screen, you noticed this waste when fixed-size windows re-scanned overlapping elements. A variable-size window takes that idea further: instead of a fixed width, the window breathes --- growing when it needs more, shrinking when it has enough.
You will drive both pointers yourself. Expand when the sum is too small, shrink when it is big enough. Watch the running sum update as you go. The critical thing to notice: which direction do the pointers move? Do they ever go backwards?
Tap a starting cell, then tap an ending cell to check a subarray. Find the shortest one with sum ≥ 7.
The variable-size window has a rhythm --- a two-phase cycle that repeats until the right pointer reaches the end of the array.
Phase 1: Expand. Move the right pointer one step rightward and add the new element to the window sum. You are growing the window because the current sum has not reached the target yet. Keep expanding until the window becomes valid (sum >= target) or you run out of elements.
Phase 2: Shrink. The window is now valid. Before expanding further, pull the left pointer rightward to see if a shorter window still works. Each shrink removes the leftmost element from the sum. Keep shrinking as long as the window remains valid --- every valid state during this phase is a candidate for the minimum-length answer.
Before you see the code --- make a prediction about the shrink direction:
When the window sum exceeds the target, what should the algorithm do?
Here is the code for LC 209 (Minimum Size Subarray Sum):
function minSubArrayLen(target: number, nums: number[]): number { let left = 0, windowSum = 0, minLen = Infinity; for (let right = 0; right < nums.length; right++) { windowSum += nums[right]; // expand while (windowSum >= target) { // shrink minLen = Math.min(minLen, right - left + 1); windowSum -= nums[left]; left++; } } return minLen === Infinity ? 0 : minLen;}Each element enters the window once (when right advances) and leaves the window once (when left advances). The outer loop runs n times. The inner while loop, across the entire execution, also runs at most n times total --- because left can only advance up to n positions before it catches up to right. Two passes through the array. That is O(n).
This is the core insight that separates sliding window from brute force. Brute force restarts left at the beginning for every new right. The variable window never backtracks --- both pointers only move rightward. That no-backtracking property is what guarantees linear time.
One more thing to notice: the answer is recorded inside the shrink loop, not after it. Every valid window encountered during shrinking is a candidate. If you record only after the loop ends, you miss the shortest valid window you found along the way.