Facebook Pixel

2256. Minimum Average Difference

Problem Description

You have an integer array nums with indices starting from 0. For each index i in the array, you need to calculate the average difference.

The average difference at index i is calculated as follows:

  • Take the average of the first i + 1 elements (elements from index 0 to i)
  • Take the average of the last n - i - 1 elements (elements from index i+1 to the end)
  • Find the absolute difference between these two averages
  • Both averages should be rounded down to the nearest integer (integer division)

Special cases to note:

  • If there are no elements in the second group (when i is the last index), the average of 0 elements is considered to be 0
  • The absolute difference means you take the positive value of the difference

Your task is to find the index that has the minimum average difference. If multiple indices have the same minimum average difference, return the smallest index.

For example, if nums = [2,5,3,9,5,3]:

  • At index 0: average of [2] is 2, average of [5,3,9,5,3] is 5, difference = |2-5| = 3
  • At index 1: average of [2,5] is 3, average of [3,9,5,3] is 5, difference = |3-5| = 2
  • At index 2: average of [2,5,3] is 3, average of [9,5,3] is 5, difference = |3-5| = 2
  • And so on...

The solution iterates through each index, maintains running sums for the left part (pre) and right part (suf), calculates the averages using integer division, and keeps track of the index with the minimum difference seen so far.

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

Intuition

The key insight is that we need to calculate averages for every possible split point in the array. A naive approach would be to recalculate the sum of elements for each split, but this would be inefficient with O(n²) time complexity.

Instead, we can observe that as we move from one index to the next, we're essentially moving one element from the "right group" to the "left group". This means:

  • The sum of the left part increases by the current element
  • The sum of the right part decreases by the same element

This observation leads us to use a sliding window technique with prefix and suffix sums. We can maintain:

  • pre: the running sum of elements we've included in the left part
  • suf: the running sum of elements remaining in the right part

Initially, pre = 0 (no elements in the left part) and suf = sum(nums) (all elements in the right part).

As we iterate through each index i:

  1. Add nums[i] to pre (include current element in left part)
  2. Subtract nums[i] from suf (remove current element from right part)
  3. Calculate the average of left part: pre // (i + 1)
  4. Calculate the average of right part: suf // (n - i - 1) if there are elements left, otherwise 0
  5. Find the absolute difference between these averages

By maintaining these running sums, we avoid recalculating sums from scratch at each index, reducing our time complexity to O(n). We simply keep track of the minimum difference found and its corresponding index, updating whenever we find a smaller difference.

Learn more about Prefix Sum patterns.

Solution Approach

We implement the solution using a single-pass traversal with running sums:

Initialization:

  • pre = 0: Tracks the sum of elements in the left part (initially empty)
  • suf = sum(nums): Tracks the sum of elements in the right part (initially contains all elements)
  • n = len(nums): Store the array length
  • ans = 0: Stores the index with minimum average difference
  • mi = inf: Tracks the minimum average difference found so far

Main Loop: We iterate through the array using enumerate to get both index i and value x:

  1. Update running sums:

    • pre += x: Add current element to left sum
    • suf -= x: Remove current element from right sum
  2. Calculate averages:

    • Left average: a = pre // (i + 1)
      • We have i + 1 elements in the left part (indices 0 to i)
    • Right average: b = 0 if n - i - 1 == 0 else suf // (n - i - 1)
      • We have n - i - 1 elements in the right part
      • Special case: when i = n - 1 (last index), there are no elements in the right part, so average is 0
  3. Update minimum:

    • Calculate absolute difference: t = abs(a - b)
    • If t < mi, update both:
      • ans = i (new minimum index)
      • mi = t (new minimum difference)

Return Result: After checking all indices, return ans which holds the index with the minimum average difference.

The algorithm achieves O(n) time complexity with a single pass through the array and O(1) extra space (not counting the input array).

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 trace through the algorithm with nums = [4, 2, 0]:

Initial Setup:

  • n = 3 (length of array)
  • pre = 0 (left sum starts empty)
  • suf = 4 + 2 + 0 = 6 (right sum has all elements)
  • ans = 0, mi = infinity

Iteration 1 (i = 0, x = 4):

  • Update sums: pre = 0 + 4 = 4, suf = 6 - 4 = 2
  • Calculate averages:
    • Left: a = 4 // 1 = 4 (average of [4])
    • Right: b = 2 // 2 = 1 (average of [2, 0])
  • Difference: t = |4 - 1| = 3
  • Since 3 < infinity: Update ans = 0, mi = 3

Iteration 2 (i = 1, x = 2):

  • Update sums: pre = 4 + 2 = 6, suf = 2 - 2 = 0
  • Calculate averages:
    • Left: a = 6 // 2 = 3 (average of [4, 2])
    • Right: b = 0 // 1 = 0 (average of [0])
  • Difference: t = |3 - 0| = 3
  • Since 3 = 3 (not less than mi): No update

Iteration 3 (i = 2, x = 0):

  • Update sums: pre = 6 + 0 = 6, suf = 0 - 0 = 0
  • Calculate averages:
    • Left: a = 6 // 3 = 2 (average of [4, 2, 0])
    • Right: b = 0 (no elements remain, so average is 0)
  • Difference: t = |2 - 0| = 2
  • Since 2 < 3: Update ans = 2, mi = 2

Result: Return ans = 2

The index 2 has the minimum average difference of 2. Notice how we efficiently maintained running sums instead of recalculating from scratch at each step, and handled the edge case where the right part becomes empty.

Solution Implementation

1class Solution:
2    def minimumAverageDifference(self, nums: List[int]) -> int:
3        # Initialize prefix sum and suffix sum
4        prefix_sum = 0
5        suffix_sum = sum(nums)
6      
7        # Get the length of the array
8        n = len(nums)
9      
10        # Initialize result index and minimum difference
11        result_index = 0
12        min_difference = float('inf')
13      
14        # Iterate through each element in the array
15        for i, num in enumerate(nums):
16            # Update prefix sum by adding current element
17            prefix_sum += num
18            # Update suffix sum by removing current element
19            suffix_sum -= num
20          
21            # Calculate average of first (i+1) elements
22            left_average = prefix_sum // (i + 1)
23          
24            # Calculate average of last (n-i-1) elements
25            # If no elements remain on the right, set average to 0
26            if n - i - 1 == 0:
27                right_average = 0
28            else:
29                right_average = suffix_sum // (n - i - 1)
30          
31            # Calculate absolute difference between left and right averages
32            current_difference = abs(left_average - right_average)
33          
34            # Update minimum difference and result index if current is smaller
35            if current_difference < min_difference:
36                result_index = i
37                min_difference = current_difference
38      
39        return result_index
40
1class Solution {
2    public int minimumAverageDifference(int[] nums) {
3        int arrayLength = nums.length;
4      
5        // Initialize prefix sum and suffix sum
6        long prefixSum = 0;
7        long suffixSum = 0;
8      
9        // Calculate total sum initially as suffix sum
10        for (int num : nums) {
11            suffixSum += num;
12        }
13      
14        // Variables to track the answer
15        int resultIndex = 0;
16        long minimumDifference = Long.MAX_VALUE;
17      
18        // Iterate through each possible split point
19        for (int i = 0; i < arrayLength; ++i) {
20            // Update prefix sum by adding current element
21            prefixSum += nums[i];
22            // Update suffix sum by removing current element
23            suffixSum -= nums[i];
24          
25            // Calculate average of left part (indices 0 to i)
26            long leftAverage = prefixSum / (i + 1);
27          
28            // Calculate average of right part (indices i+1 to n-1)
29            // Handle edge case when right part is empty
30            long rightAverage = (arrayLength - i - 1 == 0) ? 0 : suffixSum / (arrayLength - i - 1);
31          
32            // Calculate absolute difference between two averages
33            long currentDifference = Math.abs(leftAverage - rightAverage);
34          
35            // Update minimum difference and corresponding index if current is smaller
36            if (currentDifference < minimumDifference) {
37                resultIndex = i;
38                minimumDifference = currentDifference;
39            }
40        }
41      
42        return resultIndex;
43    }
44}
45
1class Solution {
2public:
3    int minimumAverageDifference(vector<int>& nums) {
4        int n = nums.size();
5        using ll = long long;
6      
7        // Initialize prefix sum and suffix sum
8        ll prefixSum = 0;
9        ll suffixSum = accumulate(nums.begin(), nums.end(), 0LL);
10      
11        // Track the index with minimum average difference
12        int resultIndex = 0;
13        ll minDifference = suffixSum;  // Initial difference when all elements are in right part
14      
15        // Iterate through each possible split position
16        for (int i = 0; i < n; ++i) {
17            // Update prefix and suffix sums
18            prefixSum += nums[i];
19            suffixSum -= nums[i];
20          
21            // Calculate average of left part (indices 0 to i)
22            ll leftAverage = prefixSum / (i + 1);
23          
24            // Calculate average of right part (indices i+1 to n-1)
25            // Handle edge case when right part is empty (i == n-1)
26            ll rightAverage = (n - i - 1 == 0) ? 0 : suffixSum / (n - i - 1);
27          
28            // Calculate absolute difference between two averages
29            ll currentDifference = abs(leftAverage - rightAverage);
30          
31            // Update minimum difference and corresponding index if current is smaller
32            if (currentDifference < minDifference) {
33                resultIndex = i;
34                minDifference = currentDifference;
35            }
36        }
37      
38        return resultIndex;
39    }
40};
41
1/**
2 * Finds the index where the absolute difference between the average of elements
3 * from start to that index and the average of remaining elements is minimized
4 * @param nums - Array of numbers to process
5 * @returns The index with minimum average difference
6 */
7function minimumAverageDifference(nums: number[]): number {
8    const arrayLength: number = nums.length;
9  
10    // Track sum of elements from start to current index
11    let prefixSum: number = 0;
12  
13    // Initialize suffix sum with total sum of all elements
14    let suffixSum: number = nums.reduce((accumulator, current) => accumulator + current, 0);
15  
16    // Track the index with minimum average difference
17    let resultIndex: number = 0;
18  
19    // Track the minimum average difference found so far
20    let minimumDifference: number = suffixSum;
21  
22    // Iterate through each possible split point
23    for (let currentIndex = 0; currentIndex < arrayLength; ++currentIndex) {
24        // Update prefix sum by adding current element
25        prefixSum += nums[currentIndex];
26      
27        // Update suffix sum by removing current element
28        suffixSum -= nums[currentIndex];
29      
30        // Calculate average of left partition (from start to current index)
31        const leftAverage: number = Math.floor(prefixSum / (currentIndex + 1));
32      
33        // Calculate average of right partition (remaining elements)
34        // Handle edge case when right partition is empty
35        const rightElementsCount: number = arrayLength - currentIndex - 1;
36        const rightAverage: number = rightElementsCount === 0 ? 0 : Math.floor(suffixSum / rightElementsCount);
37      
38        // Calculate absolute difference between the two averages
39        const currentDifference: number = Math.abs(leftAverage - rightAverage);
40      
41        // Update result if current difference is smaller than minimum found so far
42        if (currentDifference < minimumDifference) {
43            resultIndex = currentIndex;
44            minimumDifference = currentDifference;
45        }
46    }
47  
48    return resultIndex;
49}
50

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. This is because:

  • The initial sum(nums) operation takes O(n) time
  • The main loop iterates through each element exactly once, performing O(1) operations in each iteration (addition, subtraction, division, and comparison operations)
  • Therefore, the overall time complexity is O(n) + O(n) = O(n)

The space complexity is O(1). This is because:

  • The algorithm only uses a fixed number of variables (pre, suf, n, ans, mi, i, x, a, b, t) regardless of the input size
  • No additional data structures that grow with input size are created
  • All operations are performed in-place using constant extra space

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

Common Pitfalls

1. Integer Overflow with Large Arrays

Pitfall: When calculating the initial suffix_sum = sum(nums), if the array contains many large numbers, the sum might exceed the integer limits in some programming languages (though Python handles big integers automatically).

Solution: In languages with fixed integer sizes, use appropriate data types (e.g., long long in C++) or consider calculating sums incrementally if overflow is a concern.

2. Division by Zero Error

Pitfall: Forgetting to handle the special case when calculating the right average for the last index. When i = n - 1, there are no elements in the right part, leading to division by zero if not handled properly.

Solution: Always check if n - i - 1 == 0 before performing the division:

if n - i - 1 == 0:
    right_average = 0
else:
    right_average = suffix_sum // (n - i - 1)

3. Using Float Division Instead of Integer Division

Pitfall: Using regular division (/) instead of integer division (//) will produce floating-point results, which doesn't match the problem's requirement to round down to the nearest integer.

Wrong:

left_average = prefix_sum / (i + 1)  # Returns float

Correct:

left_average = prefix_sum // (i + 1)  # Returns integer (rounded down)

4. Off-by-One Errors in Element Counting

Pitfall: Incorrectly calculating the number of elements in each part. The left part has i + 1 elements (not i), and the right part has n - i - 1 elements (not n - i).

Solution: Remember that:

  • Left part includes indices [0, i] → count = i + 1
  • Right part includes indices [i+1, n-1] → count = n - i - 1

5. Not Returning the Smallest Index for Ties

Pitfall: Using <= instead of < when comparing differences, which would return the last index with minimum difference rather than the first.

Wrong:

if current_difference <= min_difference:  # Returns last index with minimum
    result_index = i
    min_difference = current_difference

Correct:

if current_difference < min_difference:  # Returns first index with minimum
    result_index = i
    min_difference = current_difference
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

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

Load More