Sliding window
On This Lesson 11 topics
Lesson Overview
A web shop records how many orders arrived in each minute. The manager asks: "In which 3 consecutive minutes were we busiest?" The slow way is to add up every group of three from scratch. But when you move from minutes 1-3 to minutes 2-4, two of the three numbers are the same. Why add them again?
A sliding window is the technique that avoids this waste. Picture a picture frame laid over a row of numbers. You slide the frame one step: one number leaves on the left, one number enters on the right, and you update the total with a single subtraction and a single addition.
In this lesson you will learn two kinds of windows: a fixed-size window (always k items) and a variable-size window (grows and shrinks to keep a rule true). Both solve "subarray" and "substring" problems, which ask about a block of consecutive items.
This lesson builds on the previous one (two pointers), on arrays and loops, and on Big O (BEG-039).
Learning Objectives
By the end of this lesson you will be able to:
- Define a window
[left, right]and compute its length asright - left + 1. - Write a fixed-size window that finds the best sum of
kconsecutive items in O(n). - Write a variable-size window that grows with
rightand shrinks withleft. - Explain why a sliding window is O(n) and not O(n × k).
- Recognize a subarray or substring problem and decide whether a window fits.
- Avoid the common off-by-one mistakes at the window edges.
Written Explanation
Subarrays and windows
A subarray is a block of consecutive items of an array. For {2, 1, 5, 1, 3, 2}, the block {1, 5, 1} (indexes 1 to 3) is a subarray, but {2, 5} is not, because it skips an item. For text, the same idea is called a substring.
A window is how we describe a subarray while we scan: two indexes, left and right, where the window contains every item from a[left] to a[right], both ends included.
Length of the window: right - left + 1. When left == right the window holds exactly one item, and right - left would wrongly say 0. This formula is the most common source of off-by-one bugs in this topic; the demo lesson shows one on purpose.
The sliding window differs from the two pointers of the last lesson in one way: here both indexes only move forward (to the right). right adds an item to the window. left removes one.
Fixed-size window
Problem: find the largest sum of k consecutive items.
Slow idea: for each start position, add k items. That is about n × k additions, O(n × k).
Window idea: keep a running sum of the current window. As the window slides one step, add the new item and subtract the item that just fell out of the frame.
static int bestSum(int[] a, int k) {
int sum = 0, best = Integer.MIN_VALUE;
for (int i = 0; i < a.length; i++) {
sum += a[i]; // item enters on the right
if (i >= k) sum -= a[i - k]; // item leaves on the left
if (i >= k - 1) best = Math.max(best, sum); // window is full
}
return best;
}Reading it line by line, with i as the right edge:
- For the first
k - 1items the window is not full yet, so we only add. - From
i = konward, the item ati - khas just left the window, so we subtract it. The window now is[i - k + 1, i], which has exactlykitems. - From
i = k - 1onward the window is full, so its sum is a valid candidate forbest.
best starts at Integer.MIN_VALUE, not 0, because with all-negative data the true maximum is below zero.
Why it is O(n)
Each item is added once (when it enters) and subtracted at most once (when it leaves). That is at most about 2n operations, regardless of k. The "slow idea" re-adds the same items again and again; the window never does. Extra space is O(1): a few variables.
Variable-size window
Often the window size is not given. Instead there is a rule, and you want the longest or shortest window that satisfies it. The pattern:
- Move
rightforward, addinga[right]to the window. - While the rule is broken, move
leftforward, removing items, until the rule holds again. - Now the window is valid; update the answer.
Problem: the longest subarray whose sum is at most limit, for non-negative numbers.
static int longestAtMost(int[] a, int limit) {
int left = 0, sum = 0, best = 0;
for (int right = 0; right < a.length; right++) {
sum += a[right]; // grow
while (sum > limit) { sum -= a[left]; left++; } // shrink until valid
best = Math.max(best, right - left + 1);
}
return best;
}It looks like a nested loop, but it is still O(n). right moves forward n times in total and left also moves forward at most n times in total, because neither ever goes back. The inner while does not restart; it continues from where left was. In total there are at most 2n pointer moves.
When does shrinking work? The while loop relies on a property: removing an item from the left makes the sum smaller, so shrinking eventually fixes the rule. That is true for non-negative numbers. With negative numbers the sum could go up when an item leaves, and this simple window does not work. This is why a good first question is "is the data non-negative?".
Is it a window problem?
Look for these signs:
- The question is about a contiguous block (subarray or substring), not an arbitrary choice of items.
- The answer is a maximum, minimum, longest, shortest, or count over such blocks.
- Adding an item or removing an item changes a running value (a sum, a count, a set) in O(1).
If the items need not be consecutive, a window is the wrong tool.
Examples
Example 1: tracing a fixed window (simple)
Array {2, 1, 5, 1, 3, 2} with k = 3. I added a print line after the update of sum and ran the loop.
i=0 sum=2
i=1 sum=3
i=2 sum=8
i=3 sum=7
i=4 sum=9
i=5 sum=6The sums at i = 2, 3, 4, 5 are the full-window sums 8, 7, 9, 6. The first two lines are the window still filling. bestSum returns 9 (the items 5, 1, 3). With the line if (i >= k) sum -= a[i - k] you can check i=3: sum was 8, add a[3] = 1 to get 9, subtract a[0] = 2 to get 7. Correct.
Example 2: a variable window (practical)
longestAtMost(new int[]{2, 1, 3, 4, 1, 1}, 6) returned 3. Walk through it:
| right | action | window | sum | length |
|---|---|---|---|---|
| 0 | add 2 | [0,0] | 2 | 1 |
| 1 | add 1 | [0,1] | 3 | 2 |
| 2 | add 3 | [0,2] | 6 | 3 |
| 3 | add 4 (sum 10, over); drop 2, 1, 3 | [3,3] | 4 | 1 |
| 4 | add 1 | [3,4] | 5 | 2 |
| 5 | add 1 | [3,5] | 6 | 3 |
The best length is 3 (twice). Notice that left only ever moved forward, never back.
Example 3: shortest block that reaches a target (realistic)
A warehouse receives orders in the sequence {2, 3, 1, 2, 4, 3} items per hour and wants the shortest run of consecutive hours with at least 7 items in total. Here the rule is "sum is at least target", and we want the minimum length, so we record the answer while the rule holds and shrink as long as it still holds:
static int minLenAtLeast(int[] a, int target) {
int left = 0, sum = 0, best = Integer.MAX_VALUE;
for (int right = 0; right < a.length; right++) {
sum += a[right];
while (sum >= target) {
best = Math.min(best, right - left + 1);
sum -= a[left++];
}
}
return best == Integer.MAX_VALUE ? 0 : best;
}Output for the data above: 2 (hours with 4 and 3). The 0 return is a deliberate "no such window" answer. Notice the difference from Example 2: there we shrank while the rule was broken; here we shrink while the rule still holds, to find the smallest valid window.
Backend Insight
Sliding windows appear in backend code more than you might expect. A rate limiter asks "how many requests did this user make in the last 60 seconds?", which is a window over time that drops old events from the left and adds new ones on the right. Monitoring dashboards compute a "moving average over the last 5 minutes" the same way, and a log scanner may look for "5 failed logins within 2 minutes".
Real systems often use a window over time rather than over array indexes, but the principle is the same: keep a running value, add what enters, remove what leaves, and never recompute from scratch. Those systems also have details this lesson does not cover (clocks, concurrency), so treat this as the core idea only.
Common Mistakes
Mistake 1: Window length off by one
- Incorrect:
int length = right - left; - Correct:
int length = right - left + 1;
Both ends are included. A window with left == right holds one item.
Mistake 2: Subtracting the wrong item
In the fixed window, the item leaving at step i is a[i - k], not a[i - k + 1] and not a[i - k - 1]. A quick check: with k = 3 and i = 3, the window is [1, 3], so item 0 left. 3 - 3 = 0.
Mistake 3: Recomputing the sum inside the loop
If your window code has an inner loop that adds all k items every time, it is correct but O(n × k). The point of the window is the running total.
Mistake 4: Using the shrinking window with negative numbers
The while (sum > limit) pattern assumes that removing an item cannot raise the sum. With negative values that assumption is false. Check the data first.
Mistake 5: Starting best at 0 for a maximum
If every window sum can be negative, 0 is a wrong answer that never appears in the data. Start with Integer.MIN_VALUE, or with the first window's sum.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. A window has left = 3 and right = 7. How many items does it contain?
Show solution A1
A1. 7 - 3 + 1 = 5 items (indexes 3, 4, 5, 6, 7).
A2. For {1, 4, 2, 10, 3} and k = 2, list every full-window sum and give the best.
Show solution A2
A2. Windows: {1,4} = 5, {4,2} = 6, {2,10} = 12, {10,3} = 13. Best is 13.
A3. For n = 1000 and k = 100, about how many additions does the "recompute each window" approach do, and how many additions and subtractions does the sliding window do?
Show solution A3
A3. Recompute: there are 901 windows of 100 items, so about 90,100 additions. Sliding window: 1000 additions and 900 subtractions, so about 1,900 operations in total.
Level B: Application
B1. bestSum(new int[]{-3, -1, -4, -2}, 2) should give -4. Explain why starting best at 0 would give the wrong answer.
Show solution B1
B1. Every window sum is negative. Starting at 0 means Math.max(0, negative) always stays 0, an answer that does not correspond to any window. With Integer.MIN_VALUE, the first real sum replaces it. The window sums are -4, -5, -6, so the best is -4, and my run printed -4.
B2. Trace longestAtMost on {2, 1, 3, 4, 1, 1} with limit 6 and confirm the answer 3. Which left value does the window have when right = 3?
Show solution B2
B2. See the table in Example 2: the answer is 3. When right = 3 the window ends at [3, 3] after dropping the items 2, 1, and 3, so left = 3.
B3. Run minLenAtLeast by hand on {2, 3, 1, 2, 4, 3} with target 7 and list every time a window is recorded.
Show solution B3
B3. Windows recorded (the sum reaches 7 or more): at right = 3 the window [0,3] sums 8, length 4; shrink drops 2 giving [1,3] = 6, stop. At right = 4: [1,4] = 10, length 4; drop 3 -> [2,4] = 7, length 3; drop 1 -> [3,4] = 6, stop. At right = 5: [3,5] = 9, length 3; drop 2 -> [4,5] = 7, length 2; drop 4 -> [5,5] = 3, stop. Shortest: 2.
Level C: Challenge
C1. Write longestOnes(int[] a): the longest run of 1s in an array of 0s and 1s if you may flip at most one 0 to a 1. Use a window that counts the zeros inside it. Test on {1, 1, 0, 1, 1, 1, 0, 1}.
Show solution C1
C1.
static int longestOnes(int[] a) {
int left = 0, zeros = 0, best = 0;
for (int right = 0; right < a.length; right++) {
if (a[right] == 0) zeros++;
while (zeros > 1) {
if (a[left] == 0) zeros--;
left++;
}
best = Math.max(best, right - left + 1);
}
return best;
}For the test array it returns 6 (indexes 0 to 5, flipping the single 0 at index 2; my run printed 6). It is O(n) time and O(1) space.
C2. An array has 1,000,000 items and k = 500,000. Roughly how many additions does recomputing each window need, and how many does the sliding window need?
Show solution C2
C2. Recompute: there are 500,001 windows of 500,000 items, about 2.5 x 10^11 (250 billion) additions. Sliding window: about 1,000,000 additions plus 500,000 subtractions. The difference is the whole reason the technique exists.
Mini Challenge
Write double maxAverage(int[] a, int k) that returns the highest average of any k consecutive items. Reuse the fixed-window code, and divide the best sum by k as a double at the end (watch out for integer division: (double) best / k).
Check your work: on {2, 1, 5, 1, 3, 2} with k = 3 the answer is 3.0 (best sum 9). Add a comment with the time complexity, and test k equal to the array length and k = 1.
Summary & Next Lesson
Key takeaways
- A window
[left, right]is a block of consecutive items; its length isright - left + 1. - A fixed-size window adds the entering item and subtracts the leaving item: O(n) instead of O(n × k).
- A variable-size window grows with
rightand shrinks withleftuntil the rule holds. Both pointers only move forward, so the cost is O(n). - The simple shrinking window needs data where removing an item cannot break the logic, such as non-negative numbers.
- Start the best value from a safe initial value such as
Integer.MIN_VALUE.
Next lesson
The next lesson, "Thinking recursively", changes direction. Instead of scanning with indexes, you will solve a problem by solving a smaller copy of it and combining the answer.
Self-check
- How many items are in a window with
left = 2andright = 2? - In the fixed window, which item leaves at step
i? - Why is the variable-size window O(n) even with a
whileloop inside thefor? - Why can't the simple shrinking window handle negative numbers?
- Is "the best sum of any 2 items" a sliding window problem? Why or why not?
Show answers
- One item:
2 - 2 + 1 = 1. - The item at index
i - k. leftandrightonly move forward and neither exceedsnmoves in total, so there are at most about 2n pointer moves.- Dropping an item from the left could raise the sum, so shrinking may not fix a broken rule.
- No. The items do not have to be consecutive, so it is not a subarray problem.
References
- Oracle,
Integer(Java SE 21 API). It definesMIN_VALUEandMAX_VALUEas the smallest and largestintvalues, which supports using them as starting values for a maximum and a minimum. - The O(n) arguments, window-length formula, and the examples are this course's own analysis. I compiled and ran all Java code in this lesson with JDK 21. I did not find a single authoritative page to cite for the sliding window technique itself.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.