Medium
题目描述
给你一个下标从 0 开始的正整数数组 nums。
你可以对数组执行以下两种操作任意次:
- 选择两个相等的元素并将它们从数组中删除。
- 选择三个相等的元素并将它们从数组中删除。
返回使数组为空所需的最少操作次数,如果无法使数组为空,则返回 -1。
示例 1:
输入:nums = [2,3,3,2,2,4,2,3,4]
输出:4
解释:我们可以执行以下操作来使数组为空:
- 对下标为 0 和 3 的元素执行第一种操作。结果数组为 nums = [3,3,2,4,2,3,4]。
- 对下标为 2 和 4 的元素执行第一种操作。结果数组为 nums = [3,3,4,3,4]。
- 对下标为 0、1 和 3 的元素执行第二种操作。结果数组为 nums = [4,4]。
- 对下标为 0 和 1 的元素执行第一种操作。结果数组为 nums = []。
可以证明我们无法在少于 4 次操作内使数组为空。
示例 2:
输入:nums = [2,1,2,2,3,3]
输出:-1
解释:无法使数组为空。
提示:
- 2 <= nums.length <= 10^5
- 1 <= nums[i] <= 10^6
解题思路
这道题的关键在于理解每种数字的频次如何通过删除操作清零。
首先统计每个数字的出现频次。如果某个数字只出现1次,那么无法清零(因为每次操作至少删除2个相同元素),直接返回-1。
对于频次大于1的数字,我们需要找到最少的操作次数组合。设某数字出现n次,我们可以用x次删除3个元素的操作和y次删除2个元素的操作,满足:3x + 2y = n。
关键观察:
- 如果n能被3整除,用 n/3 次删除3个元素的操作最优
- 如果n除以3余1,可以用1次删除2个元素的操作替换1次删除3个元素的操作(因为3+2=5,而2+2=4更少)
- 如果n除以3余2,直接加1次删除2个元素的操作
因此,对于频次为n的数字,最少操作次数为:(n + 2) / 3(向上取整)
这个公式可以统一处理所有情况:
- n % 3 == 0:操作次数为 n/3
- n % 3 == 1:操作次数为 (n-1)/3 + 1 = (n+2)/3
- n % 3 == 2:操作次数为 n/3 + 1 = (n+2)/3
最后将所有数字的最少操作次数相加即可。
代码实现
class Solution {
public:
int minOperations(vector<int>& nums) {
unordered_map<int, int> count;
for (int num : nums) {
count[num]++;
}
int operations = 0;
for (auto& [num, freq] : count) {
if (freq == 1) {
return -1;
}
operations += (freq + 2) / 3;
}
return operations;
}
};
class Solution:
def minOperations(self, nums: List[int]) -> int:
from collections import Counter
count = Counter(nums)
operations = 0
for freq in count.values():
if freq == 1:
return -1
operations += (freq + 2) // 3
return operations
public class Solution {
public int MinOperations(int[] nums) {
Dictionary<int, int> count = new Dictionary<int, int>();
foreach (int num in nums) {
count[num] = count.GetValueOrDefault(num, 0) + 1;
}
int operations = 0;
foreach (int freq in count.Values) {
if (freq == 1) {
return -1;
}
operations += (freq + 2) / 3;
}
return operations;
}
}
var minOperations = function(nums) {
const freq = {};
for (const num of nums) {
freq[num] = (freq[num] || 0) + 1;
}
let operations = 0;
for (const count of Object.values(freq)) {
if (count === 1) return -1;
operations += Math.ceil(count / 3);
}
return operations;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要遍历数组统计频次,然后遍历哈希表计算操作次数 |
| 空间复杂度 | O(k) | k为数组中不同元素的个数,用于存储哈希表 |