Minimum Average of Smallest and Largest Elements
EasyPrompt
You have an array of floating point numbers averages which is initially empty. You are given an array nums of n integers where n is even.
You repeat the following procedure n / 2 times:
- Remove the smallest element,
minElement, and the largest elementmaxElement, fromnums. - Add
(minElement + maxElement) / 2toaverages.
Return the minimum element in averages.
Example 1:
Input: nums = [7,8,3,4,15,13,4,1]
Output: 5.5
Explanation:
| step | nums | averages |
|---|---|---|
| 0 | [7,8,3,4,15,13,4,1] | [] |
| 1 | [7,8,3,4,13,4] | [8] |
| 2 | [7,8,4,4] | [8,8] |
| 3 | [7,4] | [8,8,6] |
| 4 | [] | [8,8,6,5.5] |
Example 2:
Input: nums = [1,9,8,3,10,5]
Output: 5.5
Explanation:
| step | nums | averages |
|---|---|---|
| 0 | [1,9,8,3,10,5] | [] |
| 1 | [9,8,3,5] | [5.5] |
| 2 | [8,5] | [5.5,6] |
| 3 | [] | [5.5,6,6.5] |
Example 3:
Input: nums = [1,2,3,7,8,9]
Output: 5.0
Explanation:
| step | nums | averages |
|---|---|---|
| 0 | [1,2,3,7,8,9] | [] |
| 1 | [2,3,7,8] | [5] |
| 2 | [3,7] | [5,5] |
| 3 | [] | [5,5,5] |
Constraints:
2 <= n == nums.length <= 50nis even.1 <= nums[i] <= 50
Approaches
3 approaches with complexity analysis and trade-offs.
This approach directly simulates the process described in the problem. We use a data structure that allows for dynamic resizing and element removal, such as an ArrayList. In each step, we iterate through the current list to find the minimum and maximum elements, calculate their average, and then remove them. We repeat this process until the list is empty, keeping track of the minimum average found.
Algorithm
- Convert the input array
numsinto aList<Integer>to facilitate easy element removal. - Initialize a variable
minAveragetoDouble.MAX_VALUE. - Loop
n / 2times, wherenis the initial size ofnums. - In each iteration:
- Find the minimum (
minElement) and maximum (maxElement) values in the current list. This can be done by iterating through the list or usingCollections.min()andCollections.max(). - Calculate the average:
currentAverage = (minElement + maxElement) / 2.0. - Update
minAverage = Math.min(minAverage, currentAverage). - Remove the
minElementandmaxElementfrom the list. Note that removing by value requires careful handling, especially if there are duplicates. It's safer to remove by index or uselist.remove(Integer.valueOf(value)).
- Find the minimum (
- After the loop completes, return
minAverage.
Walkthrough
import java.util.ArrayList;import java.util.Collections;import java.util.List; class Solution { public double minimumAverage(int[] nums) { List<Integer> list = new ArrayList<>(); for (int num : nums) { list.add(num); } double minAverage = Double.MAX_VALUE; int n = nums.length; for (int i = 0; i < n / 2; i++) { int minElement = Collections.min(list); int maxElement = Collections.max(list); double currentAverage = (minElement + maxElement) / 2.0; minAverage = Math.min(minAverage, currentAverage); // Remove one occurrence of minElement and maxElement list.remove(Integer.valueOf(minElement)); list.remove(Integer.valueOf(maxElement)); } return minAverage; }}Complexity
Time
O(n^2). The main loop runs `n/2` times. Inside the loop, finding the min and max elements takes `O(k)` time, where `k` is the current size of the list. Removing an element from an `ArrayList` also takes `O(k)`. Since `k` decreases from `n` down to 2, the total time complexity is a sum of an arithmetic series, resulting in `O(n^2)`.
Space
O(n). We use an `ArrayList` to store a copy of the numbers, which requires space proportional to the input size `n`.
Trade-offs
Pros
Simple to understand and implement as it directly follows the problem statement.
Cons
Inefficient due to repeated searches for min/max and costly removal operations.
A more efficient approach involves sorting the array first. Once the array is sorted, the smallest element is always at the beginning and the largest is at the end. We can use two pointers, one starting from the left end (left) and one from the right end (right), to pair the smallest and largest elements, calculate their average, and then move the pointers inward. This avoids the costly search for min and max elements in each step.
Algorithm
- Sort the input array
numsin non-decreasing order. - Initialize a variable
minAveragetoDouble.MAX_VALUE. - Initialize two pointers:
left = 0andright = nums.length - 1. - Loop while
left < right. - In each iteration:
- The current smallest element is
nums[left]and the largest isnums[right]. - Calculate their average:
currentAverage = (nums[left] + nums[right]) / 2.0. - Update
minAverage = Math.min(minAverage, currentAverage). - Move the pointers closer to the center:
left++andright--.
- The current smallest element is
- After the loop finishes,
minAveragewill hold the minimum average of all pairs. ReturnminAverage.
Walkthrough
import java.util.Arrays; class Solution { public double minimumAverage(int[] nums) { Arrays.sort(nums); double minAverage = Double.MAX_VALUE; int left = 0; int right = nums.length - 1; while (left < right) { double currentAverage = (nums[left] + nums[right]) / 2.0; minAverage = Math.min(minAverage, currentAverage); left++; right--; } return minAverage; }}Complexity
Time
O(n log n). The dominant operation is sorting the array. The subsequent two-pointer scan takes `O(n)` time.
Space
O(log n) or O(1). This depends on the implementation of the sorting algorithm. `Arrays.sort()` in Java for primitives has an average space complexity of `O(log n)` due to the recursion stack of Quicksort. If we ignore the stack space, it's considered `O(1)`.
Trade-offs
Pros
Significantly more efficient than the simulation approach.
The logic is clean and easy to follow after the initial insight about sorting.
Cons
The
O(n log n)time complexity from sorting is the bottleneck, which can be improved upon given the problem's constraints.
This is the most optimal approach, leveraging the constraint that the numbers in nums are within a small, fixed range (1 to 50). We can use a frequency array (a simplified form of counting sort) to count the occurrences of each number. Then, we use two pointers, one scanning from the smallest possible value (1) upwards and the other from the largest (50) downwards on the frequency array to find the current minimum and maximum elements to pair up.
Algorithm
- Create a frequency array,
counts, of size 51 (indices 0-50) to store the counts of each number from 1 to 50. Initialize all counts to 0. - Iterate through the input
numsarray and populate thecountsarray: for eachnum, incrementcounts[num]. - Initialize
minAveragetoDouble.MAX_VALUE. - Initialize two pointers for the values:
minVal = 1andmaxVal = 50. - Loop
n / 2times. - In each iteration:
- Find the current smallest available number by advancing
minValwhilecounts[minVal]is 0. - Find the current largest available number by decrementing
maxValwhilecounts[maxVal]is 0. - Calculate the average:
currentAverage = (minVal + maxVal) / 2.0. - Update
minAverage = Math.min(minAverage, currentAverage). - Decrement the counts for the used numbers:
counts[minVal]--andcounts[maxVal]--.
- Find the current smallest available number by advancing
- Return
minAverage.
Walkthrough
class Solution { public double minimumAverage(int[] nums) { int[] counts = new int[51]; // For numbers 1 to 50 for (int num : nums) { counts[num]++; } double minAverage = Double.MAX_VALUE; int minVal = 1; int maxVal = 50; int n = nums.length; int pairs = 0; while (pairs < n / 2) { // Find the smallest available number while (counts[minVal] == 0) { minVal++; } // Find the largest available number while (counts[maxVal] == 0) { maxVal--; } // Calculate average and update minAverage double currentAverage = (minVal + maxVal) / 2.0; minAverage = Math.min(minAverage, currentAverage); // "Remove" the elements by decrementing their counts counts[minVal]--; counts[maxVal]--; pairs++; } return minAverage; }}Complexity
Time
O(n + k), where `n` is the number of elements and `k` is the range of values (51 in this case). `O(n)` to build the frequency map, and `O(k)` for the two pointers to scan the range. This is effectively linear time.
Space
O(k). We need an auxiliary array of size `k` (where k=51) to store the frequencies.
Trade-offs
Pros
Achieves linear time complexity, which is optimal.
Very efficient for inputs where the range of values is small compared to the number of elements.
Cons
The space complexity depends on the range of values (
k), which might be large in other problems, making this approach less general.Slightly more complex to implement than the standard sorting approach.
Solutions
Solution
class Solution {public double minimumAverage(int[] nums) { Arrays.sort(nums); int n = nums.length; int ans = 1 << 30; for (int i = 0; i < n / 2; ++i) { ans = Math.min(ans, nums[i] + nums[n - i - 1]); } return ans / 2.0; }}class Solution {public: double minimumAverage(vector<int> &nums) { sort(nums.begin(), nums.end()); int ans = 1 << 30, n = nums.size(); for (int i = 0; i < n; ++i) { ans = min(ans, nums[i] + nums[n - i - 1]); } return ans / 2.0; }};class Solution: def minimumAverage(self, nums: List[int]) -> float: nums . sort() n = len(nums) return min(nums[i] + nums[n - i - 1] for i in range(n // 2)) / 2Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.