leetcode
leetcode 2451 ~ 2500
使数组为空的最少操作次数

使数组为空的最少操作次数

难度:

标签:

题目描述

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 <= 105
  • 1 <= nums[i] <= 106

代码结果

运行时间: 64 ms, 内存: 32.8 MB


/*
 * 思路:
 * 1. 使用stream流处理数组,首先统计每个元素的出现频率。
 * 2. 将频率映射为需要的操作次数,使用map和reduce方法累加总操作次数。
 * 3. 如果频率不满足条件返回-1。
 */

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

public class Solution {
    public int minOperations(int[] nums) {
        Map<Integer, Long> freqMap = Arrays.stream(nums)
            .boxed()
            .collect(Collectors.groupingBy(n -> n, Collectors.counting()));

        int operations = freqMap.values().stream()
            .mapToInt(freq -> {
                if (freq % 3 == 0) {
                    return (int) (freq / 3);
                } else if (freq % 3 == 2) {
                    return (int) ((freq - 2) / 3 + 1);
                } else if (freq % 3 == 1) {
                    if (freq < 4) return -1;
                    return (int) ((freq - 4) / 3 + 2);
                }
                return -1;
            })
            .reduce(0, (a, b) -> (a == -1 || b == -1) ? -1 : a + b);

        return operations;
    }
}

解释

方法:

这个题解首先使用了Counter类来计算数组中每个数字出现的次数。然后检查如果某个数字的出现次数为1,直接返回-1,因为单个数字无法通过题目给定的操作被删除。如果所有数字的出现次数都不为1,接下来计算最小操作次数:对于每个数字出现的次数c,使用公式(c+2)//3来计算至少需要多少次操作可以删除这些数字。这个公式是基于尽可能多地使用三个相同元素的删除操作,因为这种操作对于减少总操作次数最为有效。最后,将所有数字的操作次数相加得到结果。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在题解中检查元素出现次数为1就直接返回-1?是否存在某种操作顺序可以处理出现一次的元素?
在题目的约定中,若要删除数组中的元素,必须至少有三个相同的元素一起删除,或者两个相同的元素配合一个其他元素删除。因此,如果某个元素在数组中只出现了一次,根据题目的删除规则,无法找到有效的操作将这个单独的元素删除,因为它既不能单独形成一组也无法与其他两个元素组合删除(因为它是唯一的)。因此,如果任何元素的出现次数为1,则整个数组无法被完全清空,返回-1是符合逻辑的。
🦆
题解中使用的公式`(c+2) // 3`是怎样得来的,这个公式适用于所有情况吗?
该公式`(c+2) // 3`是基于每次最优删除操作(即每次尽可能删除三个相同的元素)得来的。通过向c(元素的出现次数)加2再整除3的方式,可以确保涵盖所有可能的最后余下的元素情况(例如余下1个或2个)。这个公式适用于c不为1的所有情况。其基本思想是通过最大化每次删除三个相同元素的次数来最小化总操作次数。对于c大于等于3的情况,这个公式非常有效。
🦆
对于出现次数大于等于2且小于3的数字,题解如何处理?例如出现2次的数字如何被消除?
对于出现次数恰好为2的数字,根据题解中使用的公式`(c+2) // 3`,计算得`(2+2) // 3 = 4 // 3 = 1`。这表明,尽管数字只出现了2次,但我们可以通过一次操作将它们删除,这需要和另一个不同的元素一起进行删除(即一次操作删除两个相同的和一个不同的元素)。因此,这种情况被有效地处理了,使得数组向着空集的方向进一步。
🦆
题解中提到`尽可能多地使用三个相同元素的删除操作`,这种策略是否总是最优?
在大多数情况下,尽可能使用三个相同元素的删除操作是最优的策略,因为它最大化了每次操作删除的元素数量,从而减少了总的操作次数。然而,这种策略假设了其他元素的出现次数也足以支持这种删除模式。在某些特殊情况下(如元素分布极不均匀时),可能需要更灵活地调整删除策略,以考虑两个相同元素配合一个其他元素的删除情况。因此,尽管这是一个高效的通用策略,但在一些边缘情况下可能需要调整。

相关问题