Solving tips
- Recognize a fixed-size window: maximizing the average of length-k subarrays is the same as maximizing the sum, so divide by k once at the end.
- Compute the first window's sum in O(k), then slide with an O(1) update: window_sum += nums[right] - nums[right-k].
- Target O(n) time and O(1) space; a prefix-sum variant is O(n)/O(n) and useful when windows are not visited in sliding order.
- Common pitfall: seed best with the first window sum or -inf (not 0), since all-negative inputs give a negative answer; the leaving index is exactly nums[right-k].
Problem
You are given an integer array nums and an integer k. Among all contiguous subarrays of exactly length k, find the one with the largest average value and return that average as a float.
Answers within 10^-5 of the true value are accepted, so ordinary floating-point division is fine.
Examples
nums = [1, 12, -5, -6, 50, 3], k = 4 β 12.75 β the window [12, -5, -6, 50] sums to 51, and 51 / 4 = 12.75.
nums = [5], k = 1 β 5.0 β the only window is the single element.
nums = [-1, -2, -3], k = 2 β -1.5 β with all-negative input the best window is [-1, -2]; the answer can be negative.
Constraints
1 <= k <= len(nums) <= 10^5
-10^4 <= nums[i] <= 10^4
Recomputing each windowβs sum from scratch costs O(nΒ·k) β about 10^10 operations in the worst case. The expected solution reuses work between adjacent windows and runs in O(n).
Think about it first
Hint 1
Maximizing the average of a fixed-length window is the same as maximizing its sum β divide by k once at the very end.
Hint 2
Two windows of length k starting at i and i + 1 overlap in all but two elements. How do their sums differ?
Hint 3
Compute the sum of the first k elements once. Then slide: for each new position, add the element entering on the right and subtract the element leaving on the left β an O(1) update. Track the maximum sum seen.
TL;DR
Fixed-size sliding window with an O(1) sum update β O(n) time, O(1) space.
Approach 1 β Brute force
For every start index, sum the k elements of that window.
class Solution:
def findMaxAverage(self, nums: list[int], k: int) -> float:
best = float("-inf")
for i in range(len(nums) - k + 1):
window_sum = sum(nums[i:i + k])
best = max(best, window_sum)
return best / k
Complexity: O(nΒ·k) time, O(k) space for the slices. With n = 10^5 and k near n/2, thatβs on the order of 10^9β10^10 additions β far too slow.
Approach 2 β Sliding window sum
The insight: adjacent windows share k - 1 elements. Sliding the window one step right changes the sum by exactly +nums[entering] - nums[leaving], so after paying O(k) once for the first window, every subsequent window costs O(1). (Maximizing the average is the same as maximizing the sum, since k is constant.)
class Solution:
def findMaxAverage(self, nums: list[int], k: int) -> float:
window_sum = sum(nums[:k])
best = window_sum
for right in range(k, len(nums)):
window_sum += nums[right] - nums[right - k]
best = max(best, window_sum)
return best / k
Walkthrough on nums = [1, 12, -5, -6, 50, 3], k = 4:
| step | window | entering | leaving | window_sum | best |
|---|
| init | [1, 12, -5, -6] | β | β | 2 | 2 |
| right=4 | [12, -5, -6, 50] | 50 | 1 | 51 | 51 |
| right=5 | [-5, -6, 50, 3] | 3 | 12 | 42 | 51 |
Return 51 / 4 = 12.75.
Complexity: O(n) time, O(1) extra space.
The insight: any window sum is a difference of two prefix sums: sum(nums[i:i+k]) = prefix[i+k] - prefix[i]. This costs O(n) extra space here, but generalizes to problems where windows are not visited in sliding order (random ranges, binary search over lengths).
from itertools import accumulate
class Solution:
def findMaxAverage(self, nums: list[int], k: int) -> float:
prefix = [0] + list(accumulate(nums))
best = max(prefix[i + k] - prefix[i] for i in range(len(nums) - k + 1))
return best / k
Complexity: O(n) time, O(n) space. Prefer the true sliding window here; keep prefix sums in your pocket for the non-sliding variants.
Common pitfalls
- Initializing
best = 0. With all-negative arrays ([-1, -2, -3], k = 2) the true answer is negative; seed best with the first windowβs sum (or -inf), never 0.
- Comparing averages instead of sums. Dividing inside the loop invites needless float error and work; since
k is fixed, compare integer sums and divide once at the end.
- Off-by-one on the leaving element. When
right enters, the element leaving is nums[right - k] β not right - k + 1 or right - k - 1. Check it on a tiny example (k = 1: entering and leaving indices must differ by exactly k).
- Fixed vs. variable: this window never grows or shrinks β there is no
while loop. If you find yourself writing shrink logic for a fixed-k problem, step back.
Pattern takeaway
The fixed-size sliding window in its purest form: when consecutive windows overlap in all but one element at each end, maintain the aggregate incrementally β +entering, -leaving β instead of recomputing it. Any aggregate that supports O(1) insert and remove (sum, count of a class of elements, hash of counts) turns an O(nΒ·k) rescan into an O(n) slide.