Minimum Number of Operations to Make Array Empty
MedPrompt
You are given a 0-indexed array nums consisting of positive integers.
There are two types of operations that you can apply on the array any number of times:
- Choose two elements with equal values and delete them from the array.
- Choose three elements with equal values and delete them from the array.
Return the minimum number of operations required to make the array empty, or -1 if it is not possible.
Example 1:
Input: nums = [2,3,3,2,2,4,2,3,4]
Output: 4
Explanation: We can apply the following operations to make the array empty:
- Apply the first operation on the elements at indices 0 and 3. The resulting array is nums = [3,3,2,4,2,3,4].
- Apply the first operation on the elements at indices 2 and 4. The resulting array is nums = [3,3,4,3,4].
- Apply the second operation on the elements at indices 0, 1, and 3. The resulting array is nums = [4,4].
- Apply the first operation on the elements at indices 0 and 1. The resulting array is nums = [].
It can be shown that we cannot make the array empty in less than 4 operations.Example 2:
Input: nums = [2,1,2,2,3,3]
Output: -1
Explanation: It is impossible to empty the array.
Constraints:
2 <= nums.length <= 1051 <= nums[i] <= 106
Note: This question is the same as 2244: Minimum Rounds to Complete All Tasks.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves sorting the array first to group identical elements together. After sorting, we can iterate through the array once to count the frequency of each number and calculate the minimum operations required for each group.
Algorithm
- Sort the input array
numsto group identical elements together. - Initialize a variable
totalOperationsto 0. - Iterate through the sorted array from left to right.
- For each unique element, count its occurrences. Let the count be
c. - If
cis 1, it's impossible to remove this element, so return -1. - To find the minimum operations for
celements, we should maximize the use of 3-element deletions. The number of operations can be calculated with the formula(c + 2) / 3. - Add the result to
totalOperations. - Continue until all elements are processed.
- Return
totalOperations.
Walkthrough
The fundamental insight is that operations only apply to elements of equal value. By sorting the array, all identical elements become adjacent, which simplifies the process of counting them. We can then iterate through the sorted array, identify blocks of identical numbers, and for each block, calculate the minimum operations needed to clear it.
For a group of count identical elements:
- If
countis 1, it's impossible to clear, so we return -1. - Otherwise, we need to express
countas2*a + 3*bsuch that the total operationsa + bis minimized. This is achieved by maximizingb(the number of 3-element deletions). - A concise mathematical formula to calculate the minimum operations for a given
count > 1is(count + 2) / 3using integer division. This covers all cases:- If
count % 3 == 0, ops =count / 3. - If
count % 3 == 1(e.g., 4), we need two 2-element ops.(4+2)/3 = 2. - If
count % 3 == 2(e.g., 5), we need one 3-element and one 2-element op.(5+2)/3 = 2.
- If
import java.util.Arrays; class Solution { public int minOperations(int[] nums) { Arrays.sort(nums); int totalOps = 0; int i = 0; while (i < nums.length) { int j = i; while (j < nums.length && nums[j] == nums[i]) { j++; } int count = j - i; if (count == 1) { return -1; } totalOps += (count + 2) / 3; i = j; } return totalOps; }}Complexity
Time
O(N log N), where N is the length of the `nums` array. The sorting step dominates the time complexity. The subsequent linear scan to count elements takes O(N) time.
Space
O(log N) to O(N). The space complexity depends on the sorting algorithm used. In Java, `Arrays.sort()` for primitive types uses a variant of Quicksort, which requires O(log N) space on average for the recursion stack, but can be O(N) in the worst case.
Trade-offs
Pros
Conceptually straightforward after realizing sorting helps group elements.
Can be implemented with a single pass after sorting.
Cons
The time complexity is dominated by the sorting step, making it less efficient than a hash map-based approach.
Sorting might require additional space depending on the algorithm used.
A more efficient approach is to use a hash map to count the frequency of each number in the array. This avoids the O(N log N) cost of sorting and allows us to process the counts directly in linear time.
Algorithm
- Create a
HashMapto store the frequency of each number in thenumsarray. - Iterate through
numsand populate the frequency map. This takes O(N) time. - Initialize a variable
totalOperationsto 0. - Iterate through the values (frequencies) in the hash map.
- For each frequency
count:- If
countis 1, it's impossible to clear the array. Return -1 immediately. - Calculate the minimum operations required for
countelements using the formula(count + 2) / 3. - Add this number to
totalOperations.
- If
- After checking all frequencies, return
totalOperations.
Walkthrough
Since the order of elements in the array is irrelevant for the operations, the problem can be reduced to a frequency counting problem. A hash map is an ideal data structure for this, as it provides average O(1) time for insertions and lookups.
First, we iterate through the input array nums once to build a map from each number to its frequency. Then, we iterate through the frequencies (the values of the map). For each frequency count, we determine the minimum number of operations.
- If any number has a frequency of 1, we can never remove it, as operations require at least two equal elements. In this case, we return -1.
- For any other frequency
count, we want to clear it using a mix of 2-element and 3-element deletions. To minimize the number of operations, we should use as many 3-element deletions as possible. The optimal number of operations for a givencountcan be calculated asceil(count / 3.0), which can be implemented in integer arithmetic as(count + 2) / 3.
This approach is optimal because it processes the array in a single pass to get counts and then processes each unique element's count once.
import java.util.HashMap;import java.util.Map; class Solution { public int minOperations(int[] nums) { Map<Integer, Integer> counts = new HashMap<>(); for (int num : nums) { counts.put(num, counts.getOrDefault(num, 0) + 1); } int totalOps = 0; for (int count : counts.values()) { if (count == 1) { return -1; } totalOps += (count + 2) / 3; } return totalOps; }}Complexity
Time
O(N), where N is the number of elements in the array. We perform a single pass to build the frequency map (O(N)) and another pass over the unique elements (at most N unique elements) to calculate the total operations. This results in an overall linear time complexity.
Space
O(K), where K is the number of unique elements in the array. In the worst-case scenario where all N elements are distinct, the space complexity becomes O(N).
Trade-offs
Pros
Optimal time complexity of O(N).
Directly solves the problem by focusing on frequencies, which is the core of the problem.
Cons
Requires extra space to store the frequency map, which can be up to O(N) if all elements are unique.
Solutions
Solution
class Solution {public int minOperations(int[] nums) { Map<Integer, Integer> count = new HashMap<>(); for (int num : nums) {class Solution {public: int minOperations(vector<int> &nums) { unordered_map<int, int> count; for (int num : nums) { ++count[num]; } int ans = 0; for (auto &[_, c] : count) { if (c < 2) { return -1; } ans += (c + 2) / 3; } return ans; }};class Solution: def minOperations(self, nums: List[int]) -> int: count = Counter(nums) ans = 0 for c in count . values(): if c == 1: return - 1 ans += (c + 2) // 3 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.