Sometimes a problem asks you to look at subarrays of an array. A subarray is just a stretch of next-to-each-other elements, like arr[2] through arr[5]. For each subarray you compute some value with a function f (a sum, a maximum, a count — whatever the problem needs), and then you combine all those values into one final answer.
The most obvious approach is to look at *every* possible subarray. You pick a starting position i, then let an ending position j walk to the right, adding the contribution of each arr[j] as it goes. This gives you every subarray that starts at i. Then you move i one step right and repeat.
For an array of size N, this means:
i at position 0, grow j across the rest of the array.i to 1, grow j again.i reaches the end.This is like running fixed-sized windows of every size, from 1 up to N, through the array. It works, but it does a lot of repeated work. Two nested loops means roughly N × N steps — slow for big arrays.
The good news: for many problems you don't need every subarray. You can skip large groups of them. That is exactly what the variable-sized sliding window technique does.
Imagine a window that sits over a piece of the array. We track its two edges with two numbers:
start — the left edge of the window.end — the right edge of the window.The window covers the subarray from start to end. Unlike a fixed window, this one can change size: it can stretch wider (expand) or shrink (contract) as it moves. We also keep one more variable:
aggregate — always holds the value of f over the current window.Because we update aggregate step by step instead of recomputing it from scratch, we can often solve in a single pass what would otherwise need nested loops.
We set start = 0 and end = 0 (a zero-sized window to begin with) and give aggregate a sensible starting value that the problem decides. Then we loop until end reaches the end of the array. In each turn of the loop we do some or all of these four operations:
aggregate with the item at end. Add the contribution of arr[end] so it becomes part of the current window's value: aggregate = f(aggregate, arr[end]).aggregate. Right now aggregate is the value of f over the subarray from start to end. Use it however the problem asks — record it, compare it, add it to a running answer.start forward). If we can safely ignore all the remaining subarrays that start at start, we increment start by 1. Before doing so, we remove arr[start]'s contribution from aggregate so the value stays correct for the smaller window.end forward). If we want to consider the next, longer subarray, we increment end by 1. We do not add the new item's contribution yet — that happens at the start of the next iteration.The key insight: in every iteration at least one of start or end moves forward, and neither ever moves backward.
start and end to 0.aggregate to an initial value the problem decides.end < arr.size():arr[end] to aggregate.aggregate.arr[start]'s contribution and increment start.end.Here is the generic C++ skeleton:
void slidingWindow(vector<int> &arr) {
// Initialize start and end to 0
int start = 0, end = 0;
// Initialize aggregate to a default value
int aggregate = 0;
// Move the window one step to the right until
// it reaches the end of the array
while(end < arr.size()) {
if (shouldComputeAggregate) {
// Add contribution of arr[end]
aggregate = f(aggregate, arr[end]);
}
// Process aggregate
// ......
// (Add your processing code here)
}
}
You fill in the shouldComputeAggregate check and the processing/contract/expand logic to match your specific problem.
Both start and end begin at 0, and in each iteration at least one of them steps forward and never goes back. So across the whole run they can move forward a combined maximum of about 2 * N times before both reach the end of the array.
2 * N iterations, which simplifies to O(N) — assuming the helper functions f and g each take constant O(1) time. In the best case end reaches the end after just N iterations, still O(N).So in both the best and the worst case: Time O(N), Space O(1) — a big improvement over the nested-loop approach.
Later in the course we will look at how to recognize which problems fit this pattern and walk through a full worked example.