Facebook Pixel

643. Maximum Average Subarray I

Problem Description

You need to find a contiguous subarray of exactly length k from an integer array nums that has the maximum average value.

Given:

  • An integer array nums with n elements
  • An integer k representing the required subarray length

The task is to:

  1. Find all possible contiguous subarrays of length k
  2. Calculate the average value for each subarray
  3. Return the maximum average value found

For example, if nums = [1, 12, -5, -6, 50, 3] and k = 4, you would check:

  • Subarray [1, 12, -5, -6] with average = 2/4 = 0.5
  • Subarray [12, -5, -6, 50] with average = 51/4 = 12.75
  • Subarray [-5, -6, 50, 3] with average = 42/4 = 10.5

The maximum average is 12.75.

The solution uses a sliding window technique where it:

  1. Calculates the sum of the first k elements
  2. Slides the window by removing the leftmost element and adding the next element to the right
  3. Keeps track of the maximum sum encountered
  4. Returns the maximum sum divided by k to get the maximum average

The answer will be accepted if the calculation error is less than 10^-5.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that to find the maximum average of a subarray of length k, we need to find the subarray with the maximum sum (since all subarrays have the same length k, the one with maximum sum will have maximum average).

A naive approach would be to calculate the sum for each possible subarray of length k starting from every position, but this would involve redundant calculations. For instance, if we have the sum of elements from index i to i+k-1, and we want the sum from index i+1 to i+k, we're recalculating the sum of overlapping elements.

This is where the sliding window technique becomes powerful. Think of it like a fixed-size window sliding across the array. When we move the window one position to the right:

  • We lose one element from the left side of the window
  • We gain one element on the right side of the window

Instead of recalculating the entire sum, we can simply update our previous sum by:

  • Subtracting the element that left the window (nums[i - k])
  • Adding the element that entered the window (nums[i])

This transforms our problem from O(n * k) time complexity (calculating sum for each window) to O(n) time complexity (single pass through the array).

Since dividing by k is a constant operation that doesn't affect which sum is maximum, we can find the maximum sum first and divide by k only once at the end to get the maximum average.

Learn more about Sliding Window patterns.

Solution Approach

The solution implements a sliding window technique to efficiently find the maximum sum of a subarray of length k, then divides by k to get the maximum average.

Step-by-step implementation:

  1. Initialize the first window: Calculate the sum of the first k elements using sum(nums[:k]). This becomes both our initial window sum s and our initial answer ans.

  2. Slide the window: Starting from index k (the (k+1)-th element), iterate through the rest of the array. For each position i:

    • Update the window sum using the formula: s = s + nums[i] - nums[i - k]
    • This adds the new element entering the window (nums[i]) and removes the element leaving the window (nums[i - k])
  3. Track the maximum: After each window update, compare the current sum s with the maximum sum found so far (ans), and update ans if necessary using ans = max(ans, s).

  4. Calculate the average: After processing all windows, divide the maximum sum by k to get the maximum average: return ans / k.

Example walkthrough:

For nums = [1, 12, -5, -6, 50, 3] and k = 4:

  • Initial window [1, 12, -5, -6]: sum = 2, ans = 2
  • Slide to include 50, remove 1: sum = 2 + 50 - 1 = 51, ans = 51
  • Slide to include 3, remove 12: sum = 51 + 3 - 12 = 42, ans = 51
  • Return 51 / 4 = 12.75

The time complexity is O(n) where n is the length of the array, and space complexity is O(1) as we only use a few variables to track the sums.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the sliding window approach.

Example: nums = [5, 2, -1, 3, 7] and k = 3

Goal: Find the contiguous subarray of length 3 with the maximum average.

Step 1: Initialize the first window

  • First window contains elements at indices 0, 1, 2: [5, 2, -1]
  • Calculate initial sum: s = 5 + 2 + (-1) = 6
  • Set this as our current maximum: ans = 6

Step 2: Slide the window to position 2

  • Remove the element leaving the window (index 0): nums[0] = 5
  • Add the element entering the window (index 3): nums[3] = 3
  • Update sum: s = 6 - 5 + 3 = 4
  • Window is now [2, -1, 3] with sum = 4
  • Update maximum: ans = max(6, 4) = 6

Step 3: Slide the window to position 3

  • Remove the element leaving the window (index 1): nums[1] = 2
  • Add the element entering the window (index 4): nums[4] = 7
  • Update sum: s = 4 - 2 + 7 = 9
  • Window is now [-1, 3, 7] with sum = 9
  • Update maximum: ans = max(6, 9) = 9

Step 4: Calculate the maximum average

  • Maximum sum found: ans = 9
  • Maximum average: 9 / 3 = 3.0

Summary of all windows checked:

  • Window 1: [5, 2, -1] → sum = 6, average = 2.0
  • Window 2: [2, -1, 3] → sum = 4, average = 1.33
  • Window 3: [-1, 3, 7] → sum = 9, average = 3.0

The maximum average is 3.0, which comes from the subarray [-1, 3, 7].

Solution Implementation

1class Solution:
2    def findMaxAverage(self, nums: List[int], k: int) -> float:
3        # Calculate the sum of the first k elements as initial window
4        window_sum = sum(nums[:k])
5      
6        # Initialize max_sum with the first window's sum
7        max_sum = window_sum
8      
9        # Slide the window through the rest of the array
10        for i in range(k, len(nums)):
11            # Update window sum by removing the leftmost element of previous window
12            # and adding the new rightmost element
13            window_sum += nums[i] - nums[i - k]
14          
15            # Update max_sum if current window sum is greater
16            max_sum = max(max_sum, window_sum)
17      
18        # Return the maximum average by dividing max sum by k
19        return max_sum / k
20
1class Solution {
2    public double findMaxAverage(int[] nums, int k) {
3        // Calculate the sum of the first k elements
4        int windowSum = 0;
5        for (int i = 0; i < k; i++) {
6            windowSum += nums[i];
7        }
8      
9        // Initialize the maximum sum with the first window's sum
10        int maxSum = windowSum;
11      
12        // Slide the window through the rest of the array
13        for (int i = k; i < nums.length; i++) {
14            // Update window sum by removing the leftmost element of previous window
15            // and adding the new rightmost element
16            windowSum += nums[i] - nums[i - k];
17          
18            // Update the maximum sum if current window sum is greater
19            maxSum = Math.max(maxSum, windowSum);
20        }
21      
22        // Return the maximum average by dividing the maximum sum by k
23        return (double) maxSum / k;
24    }
25}
26
1class Solution {
2public:
3    double findMaxAverage(vector<int>& nums, int k) {
4        // Calculate the sum of the first k elements (initial window)
5        int windowSum = accumulate(nums.begin(), nums.begin() + k, 0);
6      
7        // Initialize the maximum sum with the first window's sum
8        int maxSum = windowSum;
9      
10        // Slide the window through the rest of the array
11        for (int i = k; i < nums.size(); ++i) {
12            // Update window sum by adding new element and removing the leftmost element
13            windowSum += nums[i] - nums[i - k];
14          
15            // Update maximum sum if current window sum is greater
16            maxSum = max(maxSum, windowSum);
17        }
18      
19        // Return the maximum average by dividing max sum by k
20        return static_cast<double>(maxSum) / k;
21    }
22};
23
1/**
2 * Finds the maximum average of any contiguous subarray of length k
3 * @param nums - The input array of numbers
4 * @param k - The length of the subarray to find maximum average
5 * @returns The maximum average value
6 */
7function findMaxAverage(nums: number[], k: number): number {
8    // Calculate the sum of the first k elements
9    let currentSum: number = 0;
10    for (let i = 0; i < k; ++i) {
11        currentSum += nums[i];
12    }
13  
14    // Initialize the maximum sum with the first window's sum
15    let maxSum: number = currentSum;
16  
17    // Use sliding window technique to find the maximum sum
18    // Move the window one position at a time
19    for (let i = k; i < nums.length; ++i) {
20        // Remove the leftmost element of the previous window and add the new rightmost element
21        currentSum += nums[i] - nums[i - k];
22        // Update the maximum sum if current sum is greater
23        maxSum = Math.max(maxSum, currentSum);
24    }
25  
26    // Return the maximum average by dividing the maximum sum by k
27    return maxSum / k;
28}
29

Time and Space Complexity

Time Complexity: O(n), where n is the length of the array nums.

The algorithm first computes the sum of the first k elements using sum(nums[:k]), which takes O(k) time. Then it iterates through the remaining n - k elements in the for loop, performing constant time operations (addition, subtraction, and comparison) in each iteration. Therefore, the total time complexity is O(k) + O(n - k) = O(n).

Space Complexity: O(1)

The algorithm uses only a fixed amount of extra space regardless of the input size. It maintains three variables: ans for tracking the maximum sum, s for the current window sum, and i for the loop iterator. No additional data structures that grow with the input size are used, resulting in constant space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Division Instead of Float Division

A common mistake is performing integer division when calculating the average, which can lead to incorrect results in languages that distinguish between integer and float division.

Incorrect approach:

# In Python 2 or languages like Java/C++
return max_sum // k  # Integer division - loses precision

Correct approach:

# Ensure float division
return max_sum / k  # Python 3 automatically does float division
# Or explicitly: return float(max_sum) / k

2. Not Handling Edge Cases

Failing to consider edge cases where k equals the array length or when the array contains all negative numbers.

Problem scenario:

  • When k = len(nums), the sliding window approach still works but there's only one possible subarray
  • When all numbers are negative, initial assumptions about max_sum starting at 0 would be wrong

Solution: The provided code handles these cases correctly by:

  • Initializing max_sum with the actual first window's sum (not 0)
  • The loop naturally handles k = len(nums) by not executing (range would be empty)

3. Incorrect Window Sliding Logic

A frequent error is incorrectly calculating which element to add and which to remove when sliding the window.

Incorrect approach:

# Wrong indices for removal
window_sum += nums[i] - nums[i - k - 1]  # Off by one error
# Or
window_sum += nums[i + 1] - nums[i - k]  # Wrong addition index

Correct approach:

# At index i, we add nums[i] and remove nums[i - k]
window_sum += nums[i] - nums[i - k]

4. Recalculating Sum for Each Window

An inefficient approach that defeats the purpose of the sliding window technique.

Inefficient approach:

max_avg = float('-inf')
for i in range(len(nums) - k + 1):
    current_sum = sum(nums[i:i+k])  # O(k) operation each time
    max_avg = max(max_avg, current_sum / k)
return max_avg

Efficient approach: Use the sliding window to maintain a running sum in O(1) time per window, as shown in the provided solution.

5. Precision Issues with Floating Point Arithmetic

While the problem states the answer is accepted within 10^-5 error, repeatedly dividing by k during comparisons can accumulate floating-point errors.

Better approach: Compare sums instead of averages, then divide only once at the end:

# Compare sums throughout
max_sum = max(max_sum, window_sum)
# Divide only once at the end
return max_sum / k

This minimizes floating-point operations and potential precision loss.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following problems can be solved with backtracking (select multiple)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More