Skip to content
Ameen Abozaid
PublishedBEG-044-L02

Sliding window

  • Lesson 2 of 5
  • 12 min read
  • Beginner
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 as right - left + 1.
  • Write a fixed-size window that finds the best sum of k consecutive items in O(n).
  • Write a variable-size window that grows with right and shrinks with left.
  • 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.

java
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 - 1 items the window is not full yet, so we only add.
  • From i = k onward, the item at i - k has just left the window, so we subtract it. The window now is [i - k + 1, i], which has exactly k items.
  • From i = k - 1 onward the window is full, so its sum is a valid candidate for best.

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:

  1. Move right forward, adding a[right] to the window.
  2. While the rule is broken, move left forward, removing items, until the rule holds again.
  3. Now the window is valid; update the answer.

Problem: the longest subarray whose sum is at most limit, for non-negative numbers.

java
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.

text
i=0 sum=2
i=1 sum=3
i=2 sum=8
i=3 sum=7
i=4 sum=9
i=5 sum=6

The 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:

rightactionwindowsumlength
0add 2[0,0]21
1add 1[0,1]32
2add 3[0,2]63
3add 4 (sum 10, over); drop 2, 1, 3[3,3]41
4add 1[3,4]52
5add 1[3,5]63

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:

java
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.

java
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 is right - 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 right and shrinks with left until 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

  1. How many items are in a window with left = 2 and right = 2?
  2. In the fixed window, which item leaves at step i?
  3. Why is the variable-size window O(n) even with a while loop inside the for?
  4. Why can't the simple shrinking window handle negative numbers?
  5. Is "the best sum of any 2 items" a sliding window problem? Why or why not?
Show answers
  1. One item: 2 - 2 + 1 = 1.
  2. The item at index i - k.
  3. left and right only move forward and neither exceeds n moves in total, so there are at most about 2n pointer moves.
  4. Dropping an item from the left could raise the sum, so shrinking may not fix a broken rule.
  5. No. The items do not have to be consecutive, so it is not a subarray problem.

References

  • Oracle, Integer (Java SE 21 API). It defines MIN_VALUE and MAX_VALUE as the smallest and largest int values, 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.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.