给你一个整数数组 arr
。你可以从中选出一个整数集合,并删除这些整数在数组中的每次出现。
返回 至少 能删除数组中的一半整数的整数集合的最小大小。
示例 1:
输入:arr = [3,3,3,3,5,5,5,2,2,7]
输出:2
解释:选择 {3,7} 使得结果数组为 [5,5,5,2,2]、长度为 5(原数组长度的一半)。
大小为 2 的可行集合有 {3,5},{3,2},{5,2}。
选择 {2,7} 是不可行的,它的结果数组为 [3,3,3,3,5,5,5],新数组长度大于原数组的二分之一。
示例 2:
输入:arr = [7,7,7,7,7,7]
输出:1
解释:我们只能选择集合 {7},结果数组为空。
示例 3:
输入:arr = [1,9]
输出:1
示例 4:
输入:arr = [1000,1000,3,7]
输出:1
示例 5:
输入:arr = [1,2,3,4,5,6,7,8,9,10]
输出:5
提示:
1 <= arr.length <= 10^5
arr.length
为偶数1 <= arr[i] <= 10^5
解题思路
一个比较容易想到的思路就是采用贪心策略,分别统计所有元素出现的次数,然后从出现次数最多的元素开始取,直到取得元素个数超过len(arr)//2
。
class Solution:
def minSetSize(self, arr: List[int]) -> int:
cnt = sorted(collections.Counter(arr).values(), reverse=True)
t, n = 0, len(arr)
for i, v in enumerate(cnt):
t += v
if t >= n//2:
return i + 1
return len(cnt)
实际上还可以处理的更快。我们可以统计元素出现的次数的频率cnt
,然后将此频率按照元素出现的次数从大到小的顺序排序。这样我们就不用通过一步一步加来确定什么时候到达n//2
,而是可以通过元素出现的次数乘以频率来确定。例如:
3,3,3,3,5,5,5,2,2,7
freq: 3:4 5:3 2:2 7:1
cnt: 4:1 3:1 2:1 1:1
代码如下:
class Solution:
def minSetSize(self, arr: List[int]) -> int:
n, total, res = len(arr) // 2, 0, 0
freq = collections.Counter(arr)
cnt = collections.Counter(freq.values())
for i in sorted(cnt.keys(), reverse=True):
dif = n - total
x = min(cnt[i], (dif + i - 1)//i)
res += x
total += x * i
if total >= n:
return res
return res
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!