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.
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 partsuf
: 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
:
- Add
nums[i]
topre
(include current element in left part) - Subtract
nums[i]
fromsuf
(remove current element from right part) - Calculate the average of left part:
pre // (i + 1)
- Calculate the average of right part:
suf // (n - i - 1)
if there are elements left, otherwise 0 - 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 lengthans = 0
: Stores the index with minimum average differencemi = 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
:
-
Update running sums:
pre += x
: Add current element to left sumsuf -= x
: Remove current element from right sum
-
Calculate averages:
- Left average:
a = pre // (i + 1)
- We have
i + 1
elements in the left part (indices 0 to i)
- We have
- 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
- We have
- Left average:
-
Update minimum:
- Calculate absolute difference:
t = abs(a - b)
- If
t < mi
, update both:ans = i
(new minimum index)mi = t
(new minimum difference)
- Calculate absolute 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 EvaluatorExample 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])
- Left:
- Difference:
t = |4 - 1| = 3
- Since
3 < infinity
: Updateans = 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])
- Left:
- 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)
- Left:
- Difference:
t = |2 - 0| = 2
- Since
2 < 3
: Updateans = 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 takesO(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
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
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!