Reduce Array Size to The Half
MedPrompt
You are given an integer array arr. You can choose a set of integers and remove all the occurrences of these integers in the array.
Return the minimum size of the set so that at least half of the integers of the array are removed.
Example 1:
Input: arr = [3,3,3,3,5,5,5,2,2,7]
Output: 2
Explanation: Choosing {3,7} will make the new array [5,5,5,2,2] which has size 5 (i.e equal to half of the size of the old array).
Possible sets of size 2 are {3,5},{3,2},{5,2}.
Choosing set {2,7} is not possible as it will make the new array [3,3,3,3,5,5,5] which has a size greater than half of the size of the old array.Example 2:
Input: arr = [7,7,7,7,7,7]
Output: 1
Explanation: The only possible set you can choose is {7}. This will make the new array empty.
Constraints:
2 <= arr.length <= 105arr.lengthis even.1 <= arr[i] <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
The core idea is to be greedy. To remove at least half the elements using the minimum number of distinct integers, we should prioritize removing the integers that appear most frequently. This approach first calculates the frequency of each number, then sorts these frequencies in descending order, and finally iterates through the sorted frequencies, adding them up until the total number of removed elements reaches the target.
Algorithm
- Create a
HashMap<Integer, Integer>to store the frequency of each number. - Iterate through the input array
arrand populate the frequency map. - Extract the values (frequencies) from the map into a
List<Integer>. - Sort the list of frequencies in descending order.
- Initialize
removedCount = 0,setSize = 0, andtarget = arr.length / 2. - Iterate through the sorted frequencies from largest to smallest.
- For each frequency, add it to
removedCountand incrementsetSize. - If
removedCountbecomes greater than or equal totarget, returnsetSize.
Walkthrough
We start by counting how many times each number appears in the input array arr. A HashMap is a suitable data structure for this, mapping each number to its frequency.
Once we have all the frequencies, we don't need the numbers themselves anymore. We extract these frequencies into a list. To implement our greedy strategy, we sort this list of frequencies in descending order.
Finally, we iterate through the sorted frequencies. We maintain a running sum of removed elements (removedCount) and a count of the distinct integers we've chosen to remove (setSize). In each step, we take the next largest frequency, add it to removedCount, and increment setSize. We stop as soon as removedCount is at least half the original array's size. The value of setSize at this point is our answer.
import java.util.*; class Solution { public int minSetSize(int[] arr) { Map<Integer, Integer> counts = new HashMap<>(); for (int num : arr) { counts.put(num, counts.getOrDefault(num, 0) + 1); } List<Integer> frequencies = new ArrayList<>(counts.values()); Collections.sort(frequencies, Collections.reverseOrder()); int removedCount = 0; int setSize = 0; int target = arr.length / 2; for (int freq : frequencies) { removedCount += freq; setSize++; if (removedCount >= target) { break; } } return setSize; }}Complexity
Time
O(N log N). Counting frequencies takes O(N). If there are U unique elements, sorting their frequencies takes O(U log U). In the worst case, U can be equal to N, leading to a time complexity of O(N + N log N) which simplifies to O(N log N).
Space
O(N). The `HashMap` can store up to N unique elements, and the list of frequencies can also have a size of up to N in the worst case where all elements are unique.
Trade-offs
Pros
Intuitive and relatively straightforward to implement.
The logic directly follows the greedy strategy.
Cons
The comparison-based sort of frequencies has a time complexity of O(U log U), where U is the number of unique elements. This is the bottleneck and can be improved.
This approach optimizes the previous one by avoiding a comparison-based sort. After counting the frequencies of each number, we can use a technique similar to Bucket Sort. Since the frequencies are integers ranging from 1 to N (the length of the array), we can use an array to group numbers by their frequency. This allows us to process the frequencies in descending order in linear time.
Algorithm
- Create a
HashMap<Integer, Integer>to store the frequency of each number. - Iterate through the input array
arrand populate the frequency map. - Create a
bucketsarray of sizearr.length + 1. - Iterate through the values (frequencies) of the map. For each frequency
f, incrementbuckets[f]. - Initialize
removedCount = 0,setSize = 0, andtarget = arr.length / 2. - Iterate through the
bucketsarray from indexarr.lengthdown to 1. Let the current index bef. - If
buckets[f]is greater than 0, it means there arebuckets[f]numbers that appearftimes. - For each of these
buckets[f]numbers, addftoremovedCount, incrementsetSize, and check ifremovedCount >= target. - If the target is reached, return the current
setSize.
Walkthrough
Similar to the first approach, we begin by counting the frequency of each number using a HashMap.
The key optimization is what we do next. Instead of sorting the frequencies, we create a 'bucket' array. Let's call it buckets, of size n + 1, where n is the length of the input array. We iterate through the frequencies we calculated. For each frequency f, we increment buckets[f]. This means buckets[f] will store the count of numbers that appear f times in the original array.
Now, to greedily pick the highest frequencies, we iterate through the buckets array backwards, from n down to 1. For each frequency f, we know that there are buckets[f] numbers we can remove, each contributing f to our removed count. We process these numbers one by one, updating our removedCount and setSize, until we reach the target of removing at least n / 2 elements. This avoids the O(N log N) sorting step, leading to a linear time solution.
import java.util.*; class Solution { public int minSetSize(int[] arr) { Map<Integer, Integer> counts = new HashMap<>(); for (int num : arr) { counts.put(num, counts.getOrDefault(num, 0) + 1); } int n = arr.length; int[] buckets = new int[n + 1]; for (int freq : counts.values()) { buckets[freq]++; } int removedCount = 0; int setSize = 0; int target = n / 2; for (int freq = n; freq >= 1; freq--) { if (buckets[freq] > 0) { int numCountWithThisFreq = buckets[freq]; for (int i = 0; i < numCountWithThisFreq; i++) { removedCount += freq; setSize++; if (removedCount >= target) { return setSize; } } } } return setSize; // Should not be reached }}Complexity
Time
O(N). Counting frequencies takes O(N). Populating the buckets array takes O(U) where U is the number of unique elements (U <= N). Iterating through the buckets takes O(N). The total time complexity is O(N + U + N) which simplifies to O(N).
Space
O(N). The `HashMap` can take up to O(U) space where U is the number of unique elements. The `buckets` array takes O(N) space. The total space complexity is O(U + N), which simplifies to O(N).
Trade-offs
Pros
Achieves optimal linear time complexity, O(N).
Efficiently 'sorts' frequencies without a comparison-based algorithm.
Cons
Uses extra space for the
bucketsarray, which can be up to sizeN+1.The logic is slightly more complex than the sorting approach.
Solutions
Solution
class Solution {public int minSetSize(int[] arr) { int mx = 0; for (int x : arr) { mx = Math.max(mx, x); } int[] cnt = new int[mx + 1]; for (int x : arr) { ++cnt[x]; } Arrays.sort(cnt); int ans = 0; int m = 0; for (int i = mx;; --i) { if (cnt[i] > 0) { m += cnt[i]; ++ans; if (m * 2 >= arr.length) { return ans; } } } }}class Solution {public: int minSetSize(vector<int> &arr) { int mx = *max_element(arr.begin(), arr.end()); int cnt[mx + 1]; memset(cnt, 0, sizeof(cnt)); for (int &x : arr) { ++cnt[x]; } sort(cnt, cnt + mx + 1, greater<int>()); int ans = 0; int m = 0; for (int &x : cnt) { if (x) { m += x; ++ans; if (m * 2 >= arr.size()) { break; } } } return ans; }};class Solution: def minSetSize(self, arr: List[int]) -> int: cnt = Counter(arr) ans = m = 0 for _, v in cnt . most_common(): m += v ans += 1 if m * 2 >= len(arr): break return ansVideo 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.