The Sliding Window Technique: Fixed-Length Windows
The sliding window technique is one of the most useful patterns for problems involving arrays and strings. The idea is simple: instead of repeatedly recomputing answers for overlapping chunks of the input, you maintain a "window" that slides through the sequence, updating your answer incrementally as it moves.
In this post, we'll look at the fixed-length variant — the version you reach for when you already know the size of the subarray or substring you're looking for.
When to Use a Fixed-Length Window
If a problem says something like "find the maximum/minimum/average of every subarray of size k", that's your cue. The window size never changes; it just slides one step at a time from left to right.
The Problem: Maximum Sum Subarray of Size K
Given an integer array
numsand an integerk, find the maximum sum of any contiguous subarray of sizek.
Example:
Input: nums = [2, 1, 5, 1, 3, 2], k = 3
Output: 9
The subarray with the maximum sum is [5, 1, 3], which sums to 9.
The Naive Approach
The straightforward solution uses two loops. For every starting position i, we compute the sum of the k elements starting at i. So if i = 1 and k = 3, we sum nums[1] + nums[2] + nums[3].
func maxSumBruteForce(nums []int, k int) int {
n := len(nums)
maxSum := math.MinInt
for i := 0; i <= n-k; i++ {
currSum := 0
for j := i; j < i+k; j++ {
currSum += nums[j]
}
if currSum > maxSum {
maxSum = currSum
}
}
return maxSum
}
This works, but it runs in O(n × k) time. Notice the wasted effort: consecutive windows share k - 1 elements, yet we re-add all of them from scratch every single time.
The Optimal Solution
Look carefully at how the window moves. Going from the window ending at index end to the one ending at end + 1, only two things change:
A new element enters on the right.
The leftmost element leaves.
So instead of recomputing the whole sum, we can maintain a running windowSum: add the incoming element, and once the window has reached size k, record the result and subtract the outgoing element before sliding forward.
func maxSumSlidingWindow(nums []int, k int) int {
maxSum := math.MinInt
windowSum := 0
start := 0
for end := 0; end < len(nums); end++ {
// expand: bring the new element into the window
windowSum += nums[end]
// window has reached size k
if end-start+1 == k {
if windowSum > maxSum {
maxSum = windowSum
}
// slide: drop the leftmost element
windowSum -= nums[start]
start++
}
}
return maxSum
}
Every element is added exactly once and removed exactly once, so this runs in O(n) time with O(1) extra space.
Tracing it on our example with k = 3:
[2, 1, 5] → 8 maxSum = 8
[1, 5, 1] → 7 maxSum = 8
[5, 1, 3] → 9 maxSum = 9 ✓
[1, 3, 2] → 6 maxSum = 9
A Reusable Template
Most fixed-length sliding window problems follow the same skeleton. Only the "state" you track and the operation you perform change from problem to problem:
func fixedLengthSlidingWindow(nums []int, k int) int {
start := 0
result := 0
windowState := 0 // sum, count, frequency map, etc.
for end := 0; end < len(nums); end++ {
// 1. Add nums[end] to the window state
windowState += nums[end]
// 2. Check if the window has reached size k
if end-start+1 == k {
// 3. Update the result based on the question
result = max(result, windowState)
// 4. Slide the window: remove the leftmost element
windowState -= nums[start]
start++
}
}
return result
}
The pattern is always the same: expand from the right, do your work when the window is full, then shrink from the left. Whether the state is a running sum, a character frequency map, or a count of distinct elements, the mechanics don't change.
In a follow-up post, we'll look at the variable-length sliding window — the version you need when the window size itself depends on some condition.