2465. Number of Distinct Averages
Problem Description
You are given an integer array nums
with an even number of elements (0-indexed).
Your task is to repeatedly perform the following operations until the array is empty:
- Find and remove the minimum number from
nums
- Find and remove the maximum number from
nums
- Calculate the average of these two removed numbers using the formula
(a + b) / 2
You need to return the total number of distinct averages that you calculate through this entire process.
For example, if you have nums = [4, 1, 4, 0, 3, 5]
:
- First iteration: Remove min=0 and max=5, average =
(0 + 5) / 2 = 2.5
- Second iteration: Remove min=1 and max=4, average =
(1 + 4) / 2 = 2.5
- Third iteration: Remove min=3 and max=4, average =
(3 + 4) / 2 = 3.5
- The distinct averages are {2.5, 3.5}, so the answer is 2
Note that when there are multiple occurrences of the minimum or maximum value, you can remove any one of them.
The solution approach sorts the array first, which allows us to pair up elements from the beginning and end of the sorted array. Since we're counting distinct averages and the average depends only on the sum (a + b)
, we can track distinct sums instead. The code uses nums[i] + nums[-i - 1]
to pair the i-th smallest with the i-th largest element, iterating through half the array length.
Intuition
The key insight is recognizing that when we repeatedly remove the minimum and maximum elements from an array, we're essentially pairing up the smallest elements with the largest elements.
Think about what happens after sorting the array. The smallest element will naturally pair with the largest element, the second smallest with the second largest, and so on. This pattern continues until all elements are paired up.
For example, with a sorted array [1, 2, 3, 4, 5, 6]
:
- First removal: min=1, max=6 → pair (1, 6)
- Second removal: min=2, max=5 → pair (2, 5)
- Third removal: min=3, max=4 → pair (3, 4)
This is exactly what would happen if we sorted first and then paired elements from opposite ends of the array!
Since we only care about distinct averages, and the average is (a + b) / 2
, two pairs will have the same average only if they have the same sum. Therefore, instead of calculating and storing the actual averages (which involves division), we can simply track the distinct sums of pairs.
By sorting the array first, we can efficiently access these pairs using two pointers - one from the start (i)
and one from the end (-i-1)
. We iterate through half the array length since each iteration processes one pair, and we have n/2
pairs total for an array of length n
.
This transforms the problem from repeatedly finding min/max (which would be O(n²) overall) to a simple O(n log n) sorting followed by O(n) traversal.
Learn more about Two Pointers and Sorting patterns.
Solution Approach
The solution implements the sorting approach to efficiently find the distinct averages:
-
Sort the array: First, we sort
nums
in ascending order. This arranges elements so that we can easily access minimum and maximum values without repeated searching. -
Pair elements from opposite ends: After sorting, we use a list comprehension to pair elements:
nums[i]
gives us the i-th element from the start (smaller values)nums[-i - 1]
gives us the i-th element from the end (larger values)- We calculate the sum
nums[i] + nums[-i - 1]
for each pair
-
Iterate through half the array: The range is
range(len(nums) >> 1)
, where>> 1
is a bitwise right shift operation equivalent to dividing by 2. Since the array has even length and we process two elements per iteration, we only need to iteraten/2
times. -
Use a set to track distinct sums: The list comprehension generates all sums, and wrapping it with
set()
automatically removes duplicates. Since two pairs have the same average only if they have the same sum, counting distinct sums is equivalent to counting distinct averages. -
Return the count: Finally,
len()
gives us the number of distinct sums, which equals the number of distinct averages.
Time Complexity: O(n log n) for sorting, where n is the length of the array. The subsequent operations are O(n).
Space Complexity: O(n) for storing the set of distinct sums.
The elegance of this solution lies in recognizing that after sorting, the pairing pattern is deterministic, allowing us to avoid the repeated min/max searches that would be required in a naive simulation approach.
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 solution with nums = [1, 5, 2, 3, 8, 6]
.
Step 1: Sort the array
- Original:
[1, 5, 2, 3, 8, 6]
- After sorting:
[1, 2, 3, 5, 6, 8]
Step 2: Pair elements from opposite ends
Since the array has 6 elements, we'll iterate 3 times (len(nums) >> 1 = 6 >> 1 = 3):
-
i = 0:
nums[0] = 1
(1st from start)nums[-0-1] = nums[-1] = 8
(1st from end)- Sum = 1 + 8 = 9
-
i = 1:
nums[1] = 2
(2nd from start)nums[-1-1] = nums[-2] = 6
(2nd from end)- Sum = 2 + 6 = 8
-
i = 2:
nums[2] = 3
(3rd from start)nums[-2-1] = nums[-3] = 5
(3rd from end)- Sum = 3 + 5 = 8
Step 3: Collect distinct sums
- All sums:
[9, 8, 8]
- Convert to set to remove duplicates:
{9, 8}
- Count of distinct sums = 2
Verification with original process: If we followed the problem's original steps:
- Remove min=1, max=8 → average = (1+8)/2 = 4.5
- Remove min=2, max=6 → average = (2+6)/2 = 4.0
- Remove min=3, max=5 → average = (3+5)/2 = 4.0
Distinct averages: {4.5, 4.0} → count = 2 ✓
The solution correctly identifies 2 distinct averages by tracking distinct sums instead of calculating the actual averages.
Solution Implementation
1class Solution:
2 def distinctAverages(self, nums: List[int]) -> int:
3 # Sort the array to easily pair smallest and largest elements
4 nums.sort()
5
6 # Calculate the number of pairs (half the length of the array)
7 num_pairs = len(nums) // 2
8
9 # For each pair, calculate the sum of the smallest and largest remaining elements
10 # The sum is used instead of average since all averages would be divided by 2,
11 # which doesn't affect distinctness
12 # nums[i] pairs with nums[-i-1] (i-th from start with i-th from end)
13 sums = set(nums[i] + nums[-i - 1] for i in range(num_pairs))
14
15 # Return the count of distinct sums (which represents distinct averages)
16 return len(sums)
17
1class Solution {
2 public int distinctAverages(int[] nums) {
3 // Sort the array in ascending order
4 Arrays.sort(nums);
5
6 // Use a HashSet to store unique sums (representing distinct averages)
7 // Since average = (min + max) / 2, we only need to track unique sums
8 Set<Integer> uniqueSums = new HashSet<>();
9
10 int arrayLength = nums.length;
11
12 // Iterate through the first half of the sorted array
13 // Pair the smallest with the largest, second smallest with second largest, etc.
14 for (int i = 0; i < arrayLength / 2; i++) {
15 // Add the sum of the current smallest and largest unpaired elements
16 // nums[i] is the i-th smallest element
17 // nums[arrayLength - i - 1] is the i-th largest element
18 uniqueSums.add(nums[i] + nums[arrayLength - i - 1]);
19 }
20
21 // Return the count of distinct sums (which represents distinct averages)
22 return uniqueSums.size();
23 }
24}
25
1class Solution {
2public:
3 int distinctAverages(vector<int>& nums) {
4 // Sort the array in ascending order
5 sort(nums.begin(), nums.end());
6
7 // Use unordered_set to store unique sums (representing averages * 2)
8 // We store sums instead of averages to avoid floating point operations
9 unordered_set<int> uniqueSums;
10
11 // Get the size of the array
12 int arraySize = nums.size();
13
14 // Iterate through the first half of the sorted array
15 // Each iteration pairs the smallest remaining element with the largest
16 for (int i = 0; i < arraySize / 2; ++i) {
17 // Calculate sum of min and max elements (represents average * 2)
18 // nums[i] is the i-th smallest element
19 // nums[arraySize - i - 1] is the i-th largest element
20 int sum = nums[i] + nums[arraySize - i - 1];
21
22 // Insert the sum into the set (duplicates are automatically handled)
23 uniqueSums.insert(sum);
24 }
25
26 // Return the count of distinct averages
27 return uniqueSums.size();
28 }
29};
30
1/**
2 * Calculates the number of distinct averages that can be formed by repeatedly
3 * removing the minimum and maximum elements from the array and averaging them.
4 * @param nums - The input array of numbers
5 * @returns The count of distinct averages
6 */
7function distinctAverages(nums: number[]): number {
8 // Sort the array in ascending order
9 nums.sort((a, b) => a - b);
10
11 // Use a Set to store unique sums (averages * 2 to avoid floating point issues)
12 const uniqueSums: Set<number> = new Set();
13
14 // Get the length of the array
15 const arrayLength = nums.length;
16
17 // Iterate through the first half of the sorted array
18 // Each iteration pairs the i-th smallest with the i-th largest element
19 for (let i = 0; i < arrayLength >> 1; ++i) {
20 // Add the sum of the current min-max pair to the set
21 // Note: We store sums instead of averages since (a+b)/2 has the same
22 // uniqueness as (a+b), avoiding floating point precision issues
23 uniqueSums.add(nums[i] + nums[arrayLength - i - 1]);
24 }
25
26 // Return the number of distinct sums (which equals distinct averages)
27 return uniqueSums.size;
28}
29
Time and Space Complexity
The time complexity is O(n × log n)
, where n
is the length of the array nums
. This is dominated by the sorting operation nums.sort()
, which uses Python's Timsort algorithm with O(n × log n)
complexity. The subsequent operations include iterating through half the array (range(len(nums) >> 1)
) which is O(n/2) = O(n)
, and creating a set from the generator expression which is also O(n)
in the worst case. Since O(n × log n) + O(n) = O(n × log n)
, the overall time complexity is O(n × log n)
.
The space complexity is O(n)
. The sorting operation in Python can use up to O(n)
auxiliary space in the worst case. Additionally, the set comprehension creates a set that can contain at most n/2
elements (since we iterate n/2
times), which requires O(n)
space. The generator expression itself doesn't create additional space as it's evaluated lazily within the set constructor. Therefore, the total space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Pairing Logic with Negative Indexing
A common mistake is incorrectly pairing elements after sorting, especially when using negative indices. Developers might write:
nums[i] + nums[-i]
(incorrect) instead ofnums[i] + nums[-i - 1]
(correct)
The issue is that when i = 0
, nums[-0]
equals nums[0]
, causing the first element to be paired with itself rather than with the last element.
Solution: Always use nums[-i - 1]
to properly access the i-th element from the end. Verify with a simple example:
# For array [1, 2, 3, 4] after sorting: # i=0: nums[0]=1 pairs with nums[-0-1]=nums[-1]=4 ✓ # i=1: nums[1]=2 pairs with nums[-1-1]=nums[-2]=3 ✓
2. Using Division for Averages Instead of Sums
Some might calculate actual averages using (nums[i] + nums[-i - 1]) / 2
and store them in the set. While this works, it introduces unnecessary floating-point operations and potential precision issues.
Solution: Since all averages are divided by the same constant (2), comparing sums is sufficient for determining distinctness. Keep the implementation simple by storing sums:
# Instead of:
averages = set((nums[i] + nums[-i - 1]) / 2 for i in range(len(nums) // 2))
# Use:
sums = set(nums[i] + nums[-i - 1] for i in range(len(nums) // 2))
3. Forgetting to Handle the Even-Length Constraint
The problem guarantees an even number of elements, but developers might write defensive code for odd-length arrays, adding unnecessary complexity. Conversely, if this constraint changes in a variant problem, the current solution would fail silently.
Solution: Trust the problem constraints for contest/interview scenarios, but in production code, consider adding an assertion:
assert len(nums) % 2 == 0, "Array must have even number of elements"
4. Using Bitwise Operations Without Understanding
The code uses len(nums) >> 1
for division by 2. While this is a valid optimization, using it without understanding can lead to errors when modifying the code or when dealing with negative numbers in other contexts.
Solution: For clarity, especially in interviews, consider using len(nums) // 2
unless specifically optimizing for performance. Both achieve the same result for positive integers, but //
is more immediately readable.
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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
Want a Structured Path to Master System Design Too? Don’t Miss This!