DevLift
Back to Blog

Fixed-Size Sliding Window — Maximum Sum Subarray, Max Consecutive Ones

Stop re-summing from scratch — the fixed-size sliding window reduces O(n·k) nested loops to a single O(n) pass by adding one element and dropping one.

Admin
August 2, 20264 min read2 views

Fixed-Size Sliding Window — Maximum Sum Subarray, Max Consecutive Ones

There's a whole category of problems where the interviewer gives you an array and a number k, and asks you to find the max (or min) something across every contiguous subarray of size k. If you reach for a nested loop — outer loop picks the start, inner loop sums k elements — you're going to get O(n·k). That'll pass small inputs and fail everything else.

The fix is the fixed-size sliding window. It reduces the inner loop to a constant-time operation: instead of re-summing from scratch, you add one element on the right and drop one element on the left. One pass, O(n) total.

The Pattern

A sliding window of fixed size k moves through an array one step at a time. At each step:

  • Add the new element entering from the right: arr[right]
  • Remove the element falling off the left: arr[right - k]
  • Update your running answer
Rendering diagram...

The key mechanical insight: arr[right - k] is always the element that just exited the left edge of the window. You never need a second loop.

💡
Fixed-size sliding window works whenever your window has a fixed number of elements and you're tracking a value that updates incrementally (sum, product, frequency count).

Problem 1: Maximum Sum Subarray of Size K

Given an array of integers and a number k, find the maximum sum of any contiguous subarray of size k.

arr = [2,1,5,1,3,2], k = 3
Output: 9  (subarray [5,1,3])

Brute Force — O(n·k) Time, O(1) Space

Two loops: outer picks the window start, inner sums k elements.

// Input: arr = [2,1,5,1,3,2], k = 3
// Output: 9
const maxSumBrute = (arr, k) => {
  let maxSum = -Infinity;
 
  for (let start = 0; start <= arr.length - k; start++) {
    let windowSum = 0;
    for (let i = start; i < start + k; i++) {
      windowSum += arr[i];
    }
    maxSum = Math.max(maxSum, windowSum);
  }
 
  return maxSum;
};
// Time: O(n*k) | Space: O(1)

This works fine for small inputs. The interviewer is already thinking about what happens when n is a million and k is half a million.

Sliding Window — O(n) Time, O(1) Space

Seed the first window, then slide.

// Input: arr = [2,1,5,1,3,2], k = 3
// Output: 9
const maxSum = (arr, k) => {
  // Seed: compute sum of first window
  let windowSum = 0;
  for (let i = 0; i < k; i++) {
    windowSum += arr[i];
  }
 
  let maxSum = windowSum;
 
  // Slide the window: add right element, drop left element
  for (let right = k; right < arr.length; right++) {
    windowSum += arr[right] - arr[right - k]; // +incoming, -outgoing
    maxSum = Math.max(maxSum, windowSum);
  }
 
  return maxSum;
};
// Time: O(n) | Space: O(1)

The arr[right - k] is the element that just left the window. At right = k, the window covers indices [1, k], so arr[right - k] = arr[0] falls off. Clean.

Problem 2: Average of All Subarrays of Size K

Same structure, different output. Instead of max sum, return an array of averages for every window of size k.

arr = [1,3,-1,-3,5,3,6,7], k = 3
Output: [1.0, -0.33, 0.33, 1.67, 4.67, 5.33]
// Input: arr = [1,3,-1,-3,5,3,6,7], k = 3
// Output: [1.0, -0.33, 0.33, 1.67, 4.67, 5.33]
const findAverages = (arr, k) => {
  const result = [];
  let windowSum = 0;
 
  for (let i = 0; i < arr.length; i++) {
    windowSum += arr[i]; // Add incoming element
 
    // Once window is full, record average and drop outgoing element
    if (i >= k - 1) {
      result.push(windowSum / k);
      windowSum -= arr[i - k + 1]; // Drop leftmost element of current window
    }
  }
 
  return result;
};
// Time: O(n) | Space: O(n) for result array

Notice the slight index shift: arr[i - k + 1] is the leftmost element of the current window at index i. Either formulation works — this one builds the window incrementally rather than seeding upfront.

Problem 3: Max Consecutive Ones III

LeetCode 1004 — given a binary array nums and an integer k, return the maximum number of consecutive 1s if you can flip at most k zeros.

nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2
Output: 6

This is the interesting variation. The window isn't literally a fixed size, but the constraint is fixed: at most k zeros inside the window. When you exceed k zeros, shrink from the left until you're back within budget.

⚠️
Most people try to track which specific zeros they've "used". You don't need to. Just count zeros in the window and shrink left when the count exceeds k.
// Input: nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2
// Output: 6
const longestOnes = (nums, k) => {
  let left = 0;
  let zeroCount = 0;
  let maxLength = 0;
 
  for (let right = 0; right < nums.length; right++) {
    // Expand window: count zeros entering from the right
    if (nums[right] === 0) {
      zeroCount++;
    }
 
    // Shrink window from left when we've exceeded our zero budget
    while (zeroCount > k) {
      if (nums[left] === 0) {
        zeroCount--;
      }
      left++;
    }
 
    // Window [left, right] has at most k zeros — update max
    maxLength = Math.max(maxLength, right - left + 1);
  }
 
  return maxLength;
};
// Time: O(n) | Space: O(1)
This problem bridges fixed and variable sliding window. The window size varies, but the zero-count constraint is fixed at k. When you recognize a "budget" constraint like this, the sliding window pattern should immediately come to mind.

Let's trace through the example:

nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2
 
right=3: nums[3]=0, zeroCount=1 → window=[0..3], len=4
right=4: nums[4]=0, zeroCount=2 → window=[0..4], len=5
right=5: nums[5]=0, zeroCount=3 → exceeds k!
  shrink: left=0→1, nums[0]=1, no change to zeroCount
  shrink: left=1→2, nums[1]=1, no change
  shrink: left=2→3, nums[2]=1, no change
  shrink: left=3→4, nums[3]=0, zeroCount=2 ✓ → window=[4..5]
right=6..10: window grows to [4..9], len=6 → maxLength=6

Complexity Cheat Sheet

ProblemApproachTimeSpace
Max Sum Subarray of Size KBrute ForceO(n·k)O(1)
Max Sum Subarray of Size KSliding WindowO(n)O(1)
Average of Subarrays of Size KSliding WindowO(n)O(n)
Max Consecutive Ones IIISliding WindowO(n)O(1)

Interview Tips

State the pattern before coding. Say: "This is a fixed-size sliding window. I'll seed the first window, then on each step add the new right element and subtract the outgoing left element." This one sentence demonstrates pattern recognition and sets up the interviewer's expectations — they'll be watching you implement what you just described.

The index for the outgoing element trips people up. If your window spans [right - k + 1, right], the outgoing element is arr[right - k]. If you're iterating with a separate left pointer, you drop arr[left] before left++. Both work — pick one and be consistent.

Max Consecutive Ones III often comes up in disguise. Any problem that asks "longest subarray with at most k bad elements" is this problem. You'll see it with at-most-k odd numbers, at-most-k characters changed, etc. The window mechanism is identical: count the bad elements, and shrink left when the count exceeds the budget.

Comments (0)

No comments yet. Be the first to share your thoughts!

Related Articles

Two pointers, one pass, O(n) — learn how the variable-size sliding window grows and shrinks to solve substring and subarray problems efficiently.
AdminAugust 2, 20264 min read
Two matrix search problems, two completely different optimal approaches. Learn when to use virtual 1D binary search vs the staircase elimination — the distinction that trips up most candidates.
AdminAugust 2, 20266 min read
Reverse a Linked List — Iterative and Recursive
The three-pointer pattern is the foundation of every linked list reversal. Understand it once and LC 206, 92, and 25 become variations on the same idea.
AdminAugust 2, 20264 min read