Sliding Window

Answer questions about runs of neighbouring items in one pass: slide a window, add what enters, remove what leaves. Fixed and growing windows, traced.

15 minLesson 5 of 6
On this page
  1. The idea: a frame that slides
  2. Fixed-size windows
  3. Windows that grow and shrink
  4. Common mistakes
  5. Check yourself
  6. What to practise next

Your step counter logged a week of walking, in thousands of steps: 4, 2, 7, 1, 8, 3, 5. You want your best stretch of 3 days in a row. The obvious way is to add up days 1 to 3, then days 2 to 4, then days 3 to 5, and so on, each time from scratch.

Look at two neighbouring sums. Days 1 to 3 are 4 + 2 + 7. Days 2 to 4 are 2 + 7 + 1. The 2 + 7 in the middle is shared. Only one day left and one day arrived. So instead of re-adding everything, take the old sum, subtract the day that left, and add the day that arrived.

The idea: a frame that slides

Picture a frame laid over the array that covers a run of neighbouring items: a window. You keep a summary of what is inside it, such as a sum, a count or a set. When the window slides one step right, exactly one item enters and at most one leaves, so you update the summary with two small changes instead of rebuilding it.

4 2 7 1 8 3 5 leaves enters 13 − 4 + 1 = 10
Sliding one step: 4 leaves, 1 enters. The 2 and 7 in the middle are never added again.

Fixed-size windows

When the window size k is fixed, the recipe is short. Add up the first k items. Then, for each new item, add it and subtract the item k places behind it.

Best 3 days in a rowUse the arrows or ← → keys
sum = steps[0] + ... + steps[k - 1]best = sumfor right from k to n - 1:  sum += steps[right] - steps[right - k]  best = max(best, sum)
  1. steps
    04
    12
    27
    31
    48
    53
    65
    k
    3
    sum
    13
    best
    13

    1Add the first 3 days: 4 + 2 + 7 = 13. That is the best so far.

  2. steps
    04
    12
    27
    31right
    48
    53
    65
    k
    3
    sum
    10
    best
    13

    2Slide: 1 enters, 4 leaves. 13 + 1 − 4 = 10. Not better.

  3. steps
    04
    12
    27
    31
    48right
    53
    65
    k
    3
    sum
    16
    best
    16

    3Slide: 8 enters, 2 leaves. 10 + 8 − 2 = 16. New best.

  4. steps
    04
    12
    27
    31
    48
    53right
    65
    k
    3
    sum
    12
    best
    16

    4Slide: 3 enters, 7 leaves. 16 + 3 − 7 = 12.

  5. steps
    04
    12
    27
    31
    48
    53
    65right
    k
    3
    sum
    16
    best
    16

    5Slide: 5 enters, 1 leaves. 12 + 5 − 1 = 16. Ties the best, so best stays 16.

  6. steps
    04
    12
    27
    31
    48
    53
    65
    best
    16
    windows checked
    5

    6Done. The best 3-day total is 16, first reached on days 3 to 5 (7, 1, 8). Each slide cost one addition and one subtraction instead of re-adding 3 days. With a 30-day window it would still be 2 operations instead of 30.

1 / 6
// Largest sum of k neighbouring items. Assumes 1 <= k <= nums.length.
function bestWindowSum(nums, k) {
  let sum = 0;
  for (let i = 0; i < k; i++) sum += nums[i];
  let best = sum;
  for (let right = k; right < nums.length; right++) {
    sum += nums[right] - nums[right - k]; // one in, one out
    best = Math.max(best, sum);
  }
  return best;
}
 
bestWindowSum([4, 2, 7, 1, 8, 3, 5], 3); // 16
# Largest sum of k neighbouring items. Assumes 1 <= k <= len(nums).
def best_window_sum(nums, k):
    total = sum(nums[:k])
    best = total
    for right in range(k, len(nums)):
        total += nums[right] - nums[right - k]  # one in, one out
        best = max(best, total)
    return best
 
best_window_sum([4, 2, 7, 1, 8, 3, 5], 3)  # 16

Windows that grow and shrink

Often the size is not given. The question is "the longest run that obeys a rule" or "the shortest run that reaches a goal". Then the window's two edges move separately, like the same-direction shape in Two Pointers:

  1. Look at the next item, at right.
  2. If taking it in would break the rule, move left forward, removing items, until it would not.
  3. Take the item in. The window is valid, so record its length if it is the best so far.

Example: find the longest run of letters in "streets" with no letter repeated. The summary is a set of the letters inside the window, so "is this letter already in the window?" costs O(1).

Longest run with no repeated letterUse the arrows or ← → keys
for right from 0 to n - 1:  while s[right] is in window:    remove s[left] from window; left += 1  add s[right] to window  best = max(best, right - left + 1)
  1. s
    0sleft right
    1t
    2r
    3e
    4e
    5t
    6s
    window
    {s}
    best
    1

    1s enters. No repeats. Window length 1.

  2. s
    0sleft
    1tright
    2r
    3e
    4e
    5t
    6s
    window
    {s, t}
    best
    2

    2t enters. Length 2.

  3. s
    0sleft
    1t
    2rright
    3e
    4e
    5t
    6s
    window
    {s, t, r}
    best
    3

    3r enters. Length 3.

  4. s
    0sleft
    1t
    2r
    3eright
    4e
    5t
    6s
    window
    {s, t, r, e}
    best
    4

    4e enters. Length 4: "stre".

  5. s
    0s
    1tleft
    2r
    3e
    4eright
    5t
    6s
    window
    {t, r, e}
    best
    4

    5The next letter is e, and an e is already inside. The window must shrink from the left until the old e is gone. Remove s.

  6. s
    0s
    1t
    2rleft
    3e
    4eright
    5t
    6s
    window
    {r, e}
    best
    4

    6Still an e inside. Remove t.

  7. s
    0s
    1t
    2r
    3eleft
    4eright
    5t
    6s
    window
    {e}
    best
    4

    7Still there. Remove r.

  8. s
    0s
    1t
    2r
    3e
    4eleft right
    5t
    6s
    window
    {}
    best
    4

    8Remove the old e. Now the window is empty and the new e can come in.

  9. s
    0s
    1t
    2r
    3e
    4eleft right
    5t
    6s
    window
    {e}
    best
    4

    9Add the new e. Window "e", length 1. Best stays 4.

  10. s
    0s
    1t
    2r
    3e
    4eleft
    5tright
    6s
    window
    {e, t}
    best
    4

    10t enters. The earlier t already left, so no clash. Length 2.

  11. s
    0s
    1t
    2r
    3e
    4eleft
    5t
    6sright
    window
    {e, t, s}
    best
    4

    11s enters. Length 3: "ets".

  12. s
    0s
    1t
    2r
    3e
    4e
    5t
    6s
    best
    4
    pointer moves
    11

    12Done: the longest run is "stre", length 4. right moved 7 times and left moved 4 times: 11 moves for 7 letters, far fewer than checking all 28 runs.

1 / 12
function longestNoRepeat(s) {
  const inWindow = new Set();
  let left = 0;
  let best = 0;
  for (let right = 0; right < s.length; right++) {
    while (inWindow.has(s[right])) { // shrink until the rule holds
      inWindow.delete(s[left]);
      left++;
    }
    inWindow.add(s[right]);
    best = Math.max(best, right - left + 1);
  }
  return best;
}
 
longestNoRepeat("streets"); // 4
def longest_no_repeat(s):
    in_window = set()
    left = 0
    best = 0
    for right in range(len(s)):
        while s[right] in in_window:  # shrink until the rule holds
            in_window.remove(s[left])
            left += 1
        in_window.add(s[right])
        best = max(best, right - left + 1)
    return best
 
longest_no_repeat("streets")  # 4
ApproachTimeSpaceWhy
Recompute every windowO(n · k)O(1)k additions for each of about n windows.
Fixed windowO(n)O(1)One addition and one subtraction per slide.
Growing and shrinking windowO(n)O(n)left and right each move at most n times. The set holds only the window's letters: at most n, and never more than 26 for a to z.

The while inside the for looks like O(n²), but count the moves of left over the whole run. It only goes forward and never passes right, so it moves at most n times in total.

Common mistakes

Check yourself

Quick check

Values [5, 1, 3, 6, 2], window size 3. The first window [5, 1, 3] sums to 9. What is the sum after one slide?

The growing-and-shrinking version has a while loop inside a for loop. What is its time complexity on a string of length n?

Which task is a poor fit for "grow right, shrink left while the sum is too big"?

What to practise next

A window answers questions about runs as it slides past them. When you need the sum of many different runs, in any order, or the numbers can be negative, Prefix Sums answers each one in O(1) after a single pass.